diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3643ec0..e82fdca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,20 +53,11 @@ jobs: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push API server Docker image + - name: Build and push Graphix backend Docker image uses: docker/build-push-action@v4 with: - context: . - file: ops/api-server.dockerfile push: true - tags: ghcr.io/${{ github.repository_owner }}/graphix-api-server:latest - - name: Build and push cross-checker service Docker image - uses: docker/build-push-action@v4 - with: - context: . - file: ops/cross-checker.dockerfile - push: true - tags: ghcr.io/${{ github.repository_owner }}/graphix-cross-checker:latest + tags: ghcr.io/${{ github.repository_owner }}/graphix:latest build: name: Build diff --git a/Cargo.lock b/Cargo.lock index b9ca274..aff6ff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,41 +13,34 @@ dependencies = [ ] [[package]] -name = "ahash" -version = "0.7.6" +name = "addr2line" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "getrandom", - "once_cell", - "version_check", + "gimli", ] [[package]] -name = "ahash" -version = "0.8.3" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] -name = "allocator-api2" -version = "0.2.15" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" @@ -60,51 +53,50 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.0" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "1.0.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -133,9 +125,9 @@ checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" [[package]] name = "async-graphql" -version = "5.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0ed623e2503b45d875461e5de88a1b3466cf2ed3e43cf189a102a641b93f19" +checksum = "b16926f97f683ff3b47b035cc79622f3d6a374730b07a5d9051e81e88b5f1904" dependencies = [ "async-graphql-derive", "async-graphql-parser", @@ -148,7 +140,7 @@ dependencies = [ "fnv", "futures-util", "handlebars", - "http", + "http 1.0.0", "indexmap", "mime", "multer", @@ -159,32 +151,51 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "static_assertions", + "static_assertions_next", "tempfile", "thiserror", ] +[[package]] +name = "async-graphql-axum" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3415c9dbaf54397292da0bb81a907e2b989661ce068e4ccfebac33dc9e245e" +dependencies = [ + "async-graphql", + "async-trait", + "axum", + "bytes", + "futures-util", + "serde_json", + "tokio", + "tokio-stream", + "tokio-util", + "tower-service", +] + [[package]] name = "async-graphql-derive" -version = "5.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cebcf27112b969c4ff2a003b318ab5efde96055f9d0ee3344a3b3831fa2932ba" +checksum = "a6a7349168b79030e3172a620f4f0e0062268a954604e41475eff082380fe505" dependencies = [ "Inflector", "async-graphql-parser", - "darling", - "proc-macro-crate 1.3.1", + "darling 0.20.3", + "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "strum", + "syn 2.0.48", "thiserror", ] [[package]] name = "async-graphql-parser" -version = "5.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631770464ad2492da9af6b70048e9e477ef7c1e55fdbfb0719f3330cfa87d8e9" +checksum = "58fdc0adf9f53c2b65bb0ff5170cba1912299f248d0e48266f444b6f005deb1d" dependencies = [ "async-graphql-value", "pest", @@ -194,9 +205,9 @@ dependencies = [ [[package]] name = "async-graphql-value" -version = "5.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b59633f68ae4b858e14ec761e02455c575327249cbefed3af067a0b26d76daa9" +checksum = "7cf4d4e86208f4f9b81a503943c07e6e7f29ad3505e6c9ce6431fe64dc241681" dependencies = [ "bytes", "indexmap", @@ -204,18 +215,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "async-graphql-warp" -version = "5.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb6bf41fb7c140172034290b10c17eca6ebf37af148301f913dc95eb625083b" -dependencies = [ - "async-graphql", - "futures-util", - "serde_json", - "warp", -] - [[package]] name = "async-stream" version = "0.3.5" @@ -235,7 +234,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] @@ -246,29 +245,87 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] -name = "atoi" -version = "1.0.0" +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" dependencies = [ - "num-traits", + "async-trait", + "axum-core", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.1.0", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "axum-core" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] [[package]] -name = "base-x" -version = "0.2.11" +name = "backtrace" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "base64" @@ -278,9 +335,22 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bigdecimal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] [[package]] name = "bitflags" @@ -288,6 +358,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + [[package]] name = "block-buffer" version = "0.10.4" @@ -298,16 +374,20 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.12.0" +name = "bollard-stubs" +version = "1.42.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "ed59b5c00048f48d7af971b71f800fdf23e858844a6f9e4d32ca72e9399e7864" +dependencies = [ + "serde", + "serde_with", +] [[package]] -name = "by_address" -version = "1.1.0" +name = "bumpalo" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf8dba2868114ed769a1f2590fc9ae5eb331175b44313b6c9b922f8f7ca813d0" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" @@ -317,18 +397,21 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -338,76 +421,63 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets 0.52.0", ] [[package]] name = "chunked_transfer" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" [[package]] name = "clap" -version = "4.2.2" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.2.2" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] name = "clap_lex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" - -[[package]] -name = "codespan-reporting" -version = "0.11.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" @@ -430,9 +500,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -440,53 +510,19 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -498,143 +534,131 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.94" +name = "darling" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", + "darling_core 0.13.4", + "darling_macro 0.13.4", ] [[package]] -name = "cxx-build" -version = "1.0.94" +name = "darling" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", + "darling_core 0.20.3", + "darling_macro 0.20.3", ] [[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" +name = "darling_core" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ + "fnv", + "ident_case", "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core", - "darling_macro", + "strsim", + "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", "quote", "syn 1.0.109", ] [[package]] -name = "data-encoding" -version = "2.4.0" +name = "darling_macro" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core 0.20.3", + "quote", + "syn 2.0.48", +] [[package]] -name = "data-encoding-macro" -version = "0.1.13" +name = "data-encoding" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" -dependencies = [ - "data-encoding", - "data-encoding-macro-internal", -] +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] -name = "data-encoding-macro-internal" -version = "0.1.11" +name = "deranged" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "data-encoding", - "syn 1.0.109", + "powerfmt", ] [[package]] name = "diesel" -version = "2.0.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4391a22b19c916e50bec4d6140f29bdda3e3bb187223fe6e3ea0b6e4d1021c04" +checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" dependencies = [ - "bitflags", + "bigdecimal", + "bitflags 2.4.2", "byteorder", "chrono", "diesel_derives", + "ipnetwork", "itoa", + "libc", + "num-bigint", + "num-integer", + "num-traits", "pq-sys", "r2d2", "serde_json", + "time", + "uuid 1.7.0", ] [[package]] name = "diesel_derives" -version = "2.0.2" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad74fdcf086be3d4fdd142f67937678fe60ed431c3b2f08599e7687269410c4" +checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" dependencies = [ - "proc-macro-error", + "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] name = "diesel_migrations" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9ae22beef5e9d6fab9225ddb073c1c6c1a7a6ded5019d5da11d1e5c5adc34e2" +checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" dependencies = [ "diesel", "migrations_internals", @@ -642,42 +666,25 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "4.0.0" +name = "diesel_table_macro_syntax" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "dirs-sys", + "syn 2.0.48", ] [[package]] -name = "dirs-sys" -version = "0.3.7" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "libc", - "redox_users", - "winapi", + "block-buffer", + "crypto-common", + "subtle", ] -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - [[package]] name = "either" version = "1.8.1" @@ -686,9 +693,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -704,42 +711,19 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.3.1" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "cc", "libc", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "eventuals" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0450e5c57135f799007162ff8beba7b2809d4a018cf9cdcbca2c319a73d9d8ee" -dependencies = [ - "by_address", - "futures", - "never", - "tokio", + "windows-sys 0.52.0", ] [[package]] @@ -753,12 +737,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fnv" @@ -783,18 +764,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -807,9 +788,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -817,77 +798,55 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] -[[package]] -name = "futures-intrusive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot 0.11.2", -] - [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "futures-retry" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde5a672a61f96552aa5ed9fd9c81c3fbdae4be9b1e205d6eaf17c83705adc0f" -dependencies = [ - "futures", - "pin-project-lite", - "tokio", + "syn 2.0.48", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -913,46 +872,100 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] -name = "graphix-api-server" +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "graphix" version = "0.1.0" dependencies = [ "anyhow", "async-graphql", - "async-graphql-warp", + "async-graphql-axum", + "async-trait", + "axum", "clap", - "graphix-common", - "serde_json", - "tokio", - "tracing", + "futures", + "graphix_common_types", + "graphix_indexer_client", + "graphix_lib", + "graphix_network_sg_client", + "graphix_store", + "hex", + "nanoid", + "once_cell", + "prometheus_exporter", + "rand", + "serde_json", + "thiserror", + "tokio", + "tracing", "tracing-subscriber", - "warp", + "tracing-test", + "uuid 0.8.2", ] [[package]] -name = "graphix-common" +name = "graphix-autogen-graphql-schema" +version = "0.1.0" +dependencies = [ + "async-graphql", + "graphix_lib", +] + +[[package]] +name = "graphix_common_types" +version = "0.1.0" +dependencies = [ + "async-graphql", + "diesel", + "serde", + "serde_json", +] + +[[package]] +name = "graphix_indexer_client" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-graphql", + "async-trait", + "graphix_common_types", + "graphql_client", + "hex", + "prometheus", + "reqwest", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "graphix_lib" version = "0.1.0" dependencies = [ "anyhow", "async-graphql", "async-trait", - "chrono", "diesel", - "diesel_migrations", - "eventuals", "futures", - "futures-retry", - "graphix-common", - "graphql_client", + "graphix_common_types", + "graphix_indexer_client", + "graphix_lib", + "graphix_network_sg_client", + "graphix_store", "hex", "itertools", "once_cell", @@ -963,47 +976,48 @@ dependencies = [ "rand", "reqwest", "serde", - "serde_derive", "serde_json", "serde_yaml", - "sqlx", - "tiny-cid", "tokio", "tracing", - "uuid 1.3.3", + "url", ] [[package]] -name = "graphix-cross-checker" +name = "graphix_network_sg_client" version = "0.1.0" dependencies = [ "anyhow", - "async-trait", - "clap", - "eventuals", - "futures", - "graphix-common", + "graphix_indexer_client", "hex", - "nanoid", - "once_cell", - "prometheus_exporter", - "rand", + "prometheus", + "reqwest", + "serde", "serde_json", - "thiserror", "tokio", "tracing", - "tracing-subscriber", - "tracing-test", - "uuid 0.8.2", - "warp", + "url", ] [[package]] -name = "graphix-schema" +name = "graphix_store" version = "0.1.0" dependencies = [ + "anyhow", "async-graphql", - "graphix-common", + "chrono", + "diesel", + "diesel_migrations", + "graphix_common_types", + "graphix_indexer_client", + "hex", + "serde", + "serde_json", + "testcontainers", + "testcontainers-modules", + "tokio", + "tracing", + "uuid 1.7.0", ] [[package]] @@ -1027,9 +1041,9 @@ dependencies = [ [[package]] name = "graphql_client" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa61bb9dc6d373a8b465a5da17b62809483e8527a34b0e9034dc0915b09e160a" +checksum = "09cdf7b487d864c2939b23902291a5041bc4a84418268f25fda1c8d4e15ad8fa" dependencies = [ "graphql_query_derive", "serde", @@ -1038,9 +1052,9 @@ dependencies = [ [[package]] name = "graphql_client_codegen" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e55df64cc702c4ad6647f8df13a799ad11688a3781fadf5045f7ba12733fa9b" +checksum = "a40f793251171991c4eb75bd84bc640afa8b68ff6907bc89d3b712a22f700506" dependencies = [ "graphql-introspection-query", "graphql-parser", @@ -1055,9 +1069,9 @@ dependencies = [ [[package]] name = "graphql_query_derive" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52fc9cde811f44b15ec0692b31e56a3067f6f431c5ace712f286e47c1dacc98" +checksum = "00bda454f3d313f909298f626115092d348bc231025699f557b27e248475f48c" dependencies = [ "graphql_client_codegen", "proc-macro2", @@ -1066,16 +1080,35 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.11", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", "indexmap", "slab", "tokio", @@ -1085,9 +1118,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" dependencies = [ "log", "pest", @@ -1099,77 +1132,21 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash 0.8.3", - "allocator-api2", -] - -[[package]] -name = "hashlink" -version = "0.8.3" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" -dependencies = [ - "hashbrown 0.14.0", -] - -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "hex" @@ -1178,28 +1155,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hkdf" -version = "0.12.3" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "hmac", + "digest", ] [[package]] -name = "hmac" -version = "0.12.1" +name = "http" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ - "digest", + "bytes", + "fnv", + "itoa", ] [[package]] name = "http" -version = "0.2.9" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" dependencies = [ "bytes", "fnv", @@ -1208,12 +1187,35 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.0.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1225,23 +1227,23 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.24", + "http 0.2.11", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1253,6 +1255,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.2", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1260,34 +1281,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.28", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", + "pin-project-lite", + "socket2", + "tokio", + "tracing", +] + [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -1298,9 +1336,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1308,58 +1346,35 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" dependencies = [ - "autocfg", - "hashbrown 0.12.3", + "equivalent", + "hashbrown", "serde", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.7.2" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] -name = "is-terminal" -version = "0.4.7" +name = "ipnetwork" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", + "serde", ] [[package]] name = "itertools" -version = "0.10.5" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" dependencies = [ "either", ] @@ -1372,9 +1387,9 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -1387,30 +1402,27 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "libm" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linux-raw-sys" -version = "0.3.1" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1418,12 +1430,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "matchers" @@ -1431,17 +1440,14 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] -name = "md-5" -version = "0.10.5" +name = "matchit" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" -dependencies = [ - "digest", -] +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" @@ -1451,9 +1457,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "migrations_internals" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c493c09323068c01e54c685f7da41a9ccf9219735c3766fbfd6099806ea08fbc" +checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" dependencies = [ "serde", "toml", @@ -1461,9 +1467,9 @@ dependencies = [ [[package]] name = "migrations_macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8ff27a350511de30cdabb77147501c36ef02e0451d957abea2f30caffb2b58" +checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" dependencies = [ "migrations_internals", "proc-macro2", @@ -1477,43 +1483,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "mime_guess" -version = "2.0.4" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ - "mime", - "unicase", + "adler", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "mio" -version = "0.8.6" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "wasi", + "windows-sys 0.48.0", ] [[package]] name = "multer" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +checksum = "a15d522be0a9c3e46fd2632e272d178f56387bdb5c9fbb3a36c649062e9b5219" dependencies = [ "bytes", "encoding_rs", "futures-util", - "http", + "http 1.0.0", "httparse", "log", "memchr", @@ -1522,31 +1520,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "multibase" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b78c60039650ff12e140ae867ef5299a58e19dded4d334c849dc7177083667e2" -dependencies = [ - "base-x", - "data-encoding", - "data-encoding-macro", -] - -[[package]] -name = "multiparty" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1ec6589a6d4a1e0b33b4c0a3f6ee96dfba88ebdb3da51403fd7cf0a24a4b04" -dependencies = [ - "bytes", - "futures-core", - "httparse", - "memchr", - "pin-project-lite", - "try-lock", -] - [[package]] name = "nanoid" version = "0.4.0" @@ -1574,22 +1547,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "never" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1600,6 +1557,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1612,36 +1580,45 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.50" +version = "0.10.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" dependencies = [ - "bitflags", + "bitflags 2.4.2", "cfg-if", "foreign-types", "libc", @@ -1658,7 +1635,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] @@ -1669,9 +1646,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.85" +version = "0.9.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3d193fb1488ad46ffe3aaabc912cc931d02ee8518fe2959aea8ef52718b0c0" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" dependencies = [ "cc", "libc", @@ -1685,17 +1662,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1703,63 +1669,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.6" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", - "instant", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "winapi", + "windows-targets 0.48.5", ] -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.5.7" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.5.7" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" dependencies = [ "pest", "pest_generator", @@ -1767,22 +1714,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.7" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] name = "pest_meta" -version = "2.5.7" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" dependencies = [ "once_cell", "pest", @@ -1791,29 +1738,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1823,9 +1770,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" + +[[package]] +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" @@ -1835,22 +1788,13 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pq-sys" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b845d6d8ec554f972a2c5298aad68953fd64e7441e846075450b44656a016d1" +checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" dependencies = [ "vcpkg", ] -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -1861,35 +1805,11 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1904,7 +1824,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot 0.12.1", + "parking_lot", "protobuf", "thiserror", ] @@ -1953,9 +1873,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1967,7 +1887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", - "parking_lot 0.12.1", + "parking_lot", "scheduled-thread-pool", ] @@ -2003,74 +1923,72 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "regex" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" dependencies = [ - "bitflags", + "aho-corasick", + "memchr", + "regex-automata 0.3.7", + "regex-syntax 0.7.5", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "regex-automata" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", + "regex-syntax 0.6.29", ] [[package]] -name = "regex" -version = "1.7.3" +name = "regex-automata" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.5", ] [[package]] -name = "regex-automata" -version = "0.1.10" +name = "regex-syntax" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ - "base64 0.21.0", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.24", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-tls", "ipnet", "js-sys", @@ -2083,6 +2001,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -2093,28 +2012,30 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" -version = "0.37.11" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags", + "bitflags 2.4.2", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] -name = "rustls-pemfile" -version = "1.0.2" +name = "rustversion" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.0", -] +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" @@ -2124,11 +2045,11 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.52.0", ] [[package]] @@ -2137,34 +2058,22 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.1", + "parking_lot", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.5" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -2173,9 +2082,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -2183,22 +2092,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] @@ -2213,230 +2122,181 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_path_to_error" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" dependencies = [ - "form_urlencoded", "itoa", - "ryu", "serde", ] [[package]] -name = "serde_yaml" -version = "0.9.21" +name = "serde_spanned" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ - "indexmap", - "itoa", - "ryu", "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", ] [[package]] -name = "sharded-slab" -version = "0.1.4" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "lazy_static", + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "serde_with" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ - "libc", + "serde", + "serde_with_macros", ] [[package]] -name = "slab" -version = "0.4.8" +name = "serde_with_macros" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "autocfg", + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" +name = "serde_yaml" +version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" dependencies = [ - "libc", - "winapi", + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", ] [[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "sqlformat" -version = "0.2.1" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "itertools", - "nom", - "unicode_categories", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "sqlx" -version = "0.6.3" +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "sqlx-core", - "sqlx-macros", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "sqlx-core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" -dependencies = [ - "ahash 0.7.6", - "atoi", - "base64 0.13.1", - "bitflags", - "byteorder", - "bytes", - "crc", - "crossbeam-queue", - "dirs", - "dotenvy", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-util", - "hashlink", - "hex", - "hkdf", - "hmac", - "indexmap", - "itoa", - "libc", - "log", - "md-5", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rand", - "serde", - "serde_json", - "sha1", - "sha2", - "smallvec", - "sqlformat", - "sqlx-rt", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "whoami", +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", ] [[package]] -name = "sqlx-macros" -version = "0.6.3" +name = "signal-hook-registry" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ - "dotenvy", - "either", - "heck", - "once_cell", - "proc-macro2", - "quote", - "sha2", - "sqlx-core", - "sqlx-rt", - "syn 1.0.109", - "url", + "libc", ] [[package]] -name = "sqlx-rt" -version = "0.6.3" +name = "slab" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "native-tls", - "once_cell", - "tokio", - "tokio-native-tls", + "autocfg", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "smallvec" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] -name = "stringprep" -version = "0.1.2" +name = "socket2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "libc", + "windows-sys 0.48.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "static_assertions_next" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7beae5182595e9a8b683fa98c4317f956c9a2dec3b9716990d20023cc60c766" + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.48", +] + [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -2451,9 +2311,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -2461,37 +2321,69 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] name = "tempfile" -version = "3.5.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "testcontainers" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d2931d7f521af5bae989f716c3fa43a6af9af7ec7a5e21b59ae40878cec00" +dependencies = [ + "bollard-stubs", + "futures", + "hex", + "hmac", + "log", + "rand", + "serde", + "serde_json", + "sha2", +] + +[[package]] +name = "testcontainers-modules" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "fd5c0d66f296de264c0593f42ea2ef2ad3ff9a62bc946a5d4e8d21be1317c505" dependencies = [ - "winapi-util", + "testcontainers", ] [[package]] @@ -2511,7 +2403,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] @@ -2526,22 +2418,13 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ + "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -2549,55 +2432,19 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" dependencies = [ "time-core", ] -[[package]] -name = "tiny-cid" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add6ab1597c883a38fc0ade108679461b297ab696d417151b85996047a605c81" -dependencies = [ - "multibase", - "tiny-multihash", - "unsigned-varint", -] - -[[package]] -name = "tiny-multihash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de7d692794abd4dbba87073e4701150029887b01993e4d152d7ad3376701f50d" -dependencies = [ - "generic-array", - "tiny-multihash-derive", - "unsigned-varint", -] - -[[package]] -name = "tiny-multihash-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010febb03c87ee95ee6823f001345e92ddc073e85e0b8873ff725e261b4e5e0d" -dependencies = [ - "proc-macro-crate 0.1.5", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - [[package]] name = "tiny_http" version = "0.10.0" @@ -2607,7 +2454,7 @@ dependencies = [ "ascii 1.1.0", "chunked_transfer", "log", - "time 0.3.21", + "time", "url", ] @@ -2628,32 +2475,32 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", - "parking_lot 0.12.1", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.48", ] [[package]] @@ -2668,9 +2515,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -2679,9 +2526,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.18.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", @@ -2691,12 +2538,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -2705,30 +2553,60 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -2737,11 +2615,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2750,20 +2627,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2771,20 +2648,20 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -2823,20 +2700,20 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.18.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ - "base64 0.13.1", "byteorder", "bytes", - "http", + "data-encoding", + "http 1.0.0", "httparse", "log", "rand", @@ -2848,30 +2725,21 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "unicase" -version = "2.6.0" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2888,30 +2756,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - [[package]] name = "unreachable" version = "1.0.0" @@ -2923,21 +2767,15 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" - -[[package]] -name = "unsigned-varint" -version = "0.5.1" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fdeedbf205afadfe39ae559b75c3240f24e257d0ca27e85f85cb82aa19ac35" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "url" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -2967,12 +2805,11 @@ dependencies = [ [[package]] name = "uuid" -version = "1.3.3" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "getrandom", - "serde", ] [[package]] @@ -3001,51 +2838,13 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] -[[package]] -name = "warp" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "headers", - "http", - "hyper", - "log", - "mime", - "mime_guess", - "multiparty", - "percent-encoding", - "pin-project", - "rustls-pemfile", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tokio-util", - "tower-service", - "tracing", -] - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3054,9 +2853,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3064,24 +2863,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -3091,9 +2890,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3101,43 +2900,33 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "whoami" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - [[package]] name = "winapi" version = "0.3.9" @@ -3154,15 +2943,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3170,175 +2950,161 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.0", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.48.5", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.52.0", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.4.1" +version = "0.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 2af26e4..a848b85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,3 @@ [workspace] -members = [ - "backend/crates/*", - #"frontend" -] +resolver = "2" +members = ["crates/*"] diff --git a/ops/api-server.dockerfile b/Dockerfile similarity index 76% rename from ops/api-server.dockerfile rename to Dockerfile index a26fb97..43354e3 100644 --- a/ops/api-server.dockerfile +++ b/Dockerfile @@ -22,15 +22,15 @@ COPY --from=planner /app/recipe.json recipe.json RUN apt-get update && apt-get install -y libpq-dev ca-certificates pkg-config libssl-dev # Use cargo-chef to compile dependencies only - this will be cached by Docker. -RUN cargo chef cook --profile $CARGO_PROFILE --recipe-path recipe.json --bin graphix-api-server +RUN cargo chef cook --profile $CARGO_PROFILE --recipe-path recipe.json --bin graphix # ... and then build the rest of the application. COPY . . -RUN cargo build --profile $CARGO_PROFILE --bin graphix-api-server +RUN cargo build --profile $CARGO_PROFILE --bin graphix # Instead of calculating where the binary is located based on $CARGO_PROFILE, we # simply try to copy both `debug` and `release` binaries. -RUN cp target/release/graphix-api-server /usr/local/bin | true && \ - cp target/debug/graphix-api-server /usr/local/bin | true +RUN cp target/release/graphix /usr/local/bin | true && \ + cp target/debug/graphix /usr/local/bin | true FROM debian:bullseye-slim @@ -40,10 +40,10 @@ RUN apt-get update && \ apt-get install -y libpq-dev ca-certificates libssl-dev && \ apt-get clean -COPY --from=builder /usr/local/bin/graphix-api-server /usr/local/bin +COPY --from=builder /usr/local/bin/graphix /usr/local/bin ENV RUST_LOG="graphix=debug" EXPOSE 3030 -ENTRYPOINT [ "graphix-api-server" ] +ENTRYPOINT [ "graphix" ] diff --git a/backend/crates/api-server/Cargo.toml b/backend/crates/api-server/Cargo.toml deleted file mode 100644 index 8ccb422..0000000 --- a/backend/crates/api-server/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "graphix-api-server" -version = "0.1.0" -authors = ["Jannis Pohlmann "] -edition = "2021" -default-run = "graphix-api-server" - -[[bin]] -name = "graphix-api-server" -path = "src/main.rs" - -[dependencies] -anyhow = "1.0.52" -async-graphql = "5" -async-graphql-warp = "5" -clap = { version = "4", features = ["derive", "env"] } -graphix-common = { path = "../common" } -serde_json = "1" -tokio = { version = "1.15.0", features = ["full"] } -tracing = "0.1.29" -tracing-subscriber = { version = "0.3.6", features = ["env-filter"] } -warp = "0.3.2" diff --git a/backend/crates/api-server/src/main.rs b/backend/crates/api-server/src/main.rs deleted file mode 100644 index 524d5d5..0000000 --- a/backend/crates/api-server/src/main.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::convert::Infallible; -use std::future::Future; - -use async_graphql::http::{playground_source, GraphQLPlaygroundConfig}; -use async_graphql::Request; -use async_graphql_warp::{self, GraphQLResponse}; -use clap::Parser; -use graphix_common::graphql_api::{self}; -use graphix_common::store::Store; -use warp::http::{self, Method}; -use warp::Filter; - -#[derive(Parser, Debug)] -pub struct CliOptions { - #[clap(long, default_value = "80", env = "GRAPHIX_PORT")] - pub port: u16, - - #[clap( - long, - default_value = "postgresql://localhost:5432/graphix", - env = "GRAPHIX_DATABASE_URL" - )] - pub database_url: String, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - init_tracing(); - - let cli_options = CliOptions::parse(); - let server_fut = create_server(cli_options); - - // Listen to requests forever. - Ok(server_fut.await?.await) -} - -async fn create_server(cli_options: CliOptions) -> anyhow::Result> { - let store = Store::new(cli_options.database_url.as_str()).await?; - - // GET / -> 200 OK - let health_check_route = warp::path::end().map(|| format!("Ready to roll!")); - - // GraphQL API - let api_context = graphql_api::ApiSchemaContext { store }; - let api_schema = graphql_api::api_schema(api_context); - let api = async_graphql_warp::graphql(api_schema).and_then( - |(schema, request): (graphql_api::ApiSchema, Request)| async move { - Ok::<_, Infallible>(GraphQLResponse::from(schema.execute(request).await)) - }, - ); - let cors = warp::cors() - .allow_methods(&[Method::GET, Method::POST, Method::OPTIONS]) - .allow_header("content-type") - .allow_any_origin(); - let graphql_route = warp::any() - .and(warp::path("graphql").and(warp::path::end())) - .and(api) - .with(cors.clone()); - - // GraphQL playground - let graphql_playground = warp::get().map(|| { - http::Response::builder() - .header("content-type", "text/html") - .body(playground_source(GraphQLPlaygroundConfig::new("/graphql"))) - }); - let graphql_playground_route = warp::get() - .and(warp::path("graphql").and(warp::path::end())) - .and(graphql_playground); - - let routes = warp::get() - .and(health_check_route) - .or(graphql_playground_route) - .or(graphql_route); - - Ok(warp::serve(routes).run(([0, 0, 0, 0], cli_options.port))) -} - -fn init_tracing() { - tracing_subscriber::fmt::init(); -} diff --git a/backend/crates/common/Cargo.toml b/backend/crates/common/Cargo.toml deleted file mode 100644 index 7ea18df..0000000 --- a/backend/crates/common/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "graphix-common" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow = "1.0.48" -async-graphql = "5" -async-trait = "0.1.52" -chrono = "0.4.19" -diesel = { version = "2.0", features = ["postgres", "r2d2", "chrono", "serde_json"] } -diesel_migrations = "2.0" -eventuals = "0.6.6" -futures = "0.3.18" -futures-retry = "0.6.0" -graphql_client = "0.12.0" -hex = "0.4.3" -itertools = "0.10.3" -prometheus = "0.13.3" -prometheus_exporter = "0.8.5" -reqwest = { version = "0.11.6", features = ["json"] } -serde = { version = "1.0.130", features = ["derive"] } -serde_derive = "1.0.130" -serde_json = "1.0.78" -serde_yaml = "0.9" -tiny-cid = "0.3.0" -tokio = { version = "1.14.0", features = ["full"] } -tracing = "0.1.29" -rand = { version = "0.8.4", features = ["small_rng"], optional = true } -sqlx = { version = "0.6", features = ["postgres", "runtime-tokio-native-tls"] } -once_cell = { version = "1.9.0", optional = true } -uuid = { version = "1.3", features = ["serde", "v4"] } - -[features] -tests = ["rand", "once_cell"] - -[build-dependencies] -reqwest = { version = "0.11", features = ["blocking"] } - -[dev-dependencies] -graphix-common = { path = ".", features = ["tests"] } -once_cell = { version = "1.9.0" } -quickcheck = "1" -quickcheck_macros = "1" -rand = { version = "0.8.4", features = ["small_rng"] } diff --git a/backend/crates/common/migrations/.gitkeep b/backend/crates/common/migrations/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/backend/crates/common/src/graphql_api/mod.rs b/backend/crates/common/src/graphql_api/mod.rs deleted file mode 100644 index e63d76b..0000000 --- a/backend/crates/common/src/graphql_api/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod server; -pub mod types; - -pub use server::*; diff --git a/backend/crates/common/src/indexer/mod.rs b/backend/crates/common/src/indexer/mod.rs deleted file mode 100644 index 7760219..0000000 --- a/backend/crates/common/src/indexer/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -mod interceptor; -mod real_indexer; - -use std::collections::HashMap; -use std::fmt::Debug; -use std::hash::Hash; -use std::sync::Arc; - -use anyhow::anyhow; -use async_trait::async_trait; -pub use interceptor::IndexerInterceptor; -pub use real_indexer::RealIndexer; - -use crate::types::{IndexingStatus, PoiRequest, ProofOfIndexing}; - -#[async_trait] -pub trait Indexer: Send + Sync + Debug { - /// Uniquely identifies the indexer. This is relied on for the [`Hash`] and - /// [`Eq`] impls. - fn id(&self) -> &str; - - fn address(&self) -> Option<&[u8]>; - - async fn ping(self: Arc) -> anyhow::Result<()>; - - async fn indexing_statuses(self: Arc) -> anyhow::Result>; - - async fn proofs_of_indexing(self: Arc, requests: Vec) - -> Vec; - - /// Convenience wrapper around calling [`Indexer::proofs_of_indexing`] for a - /// single POI. - async fn proof_of_indexing( - self: Arc, - request: PoiRequest, - ) -> Result { - let pois = self.proofs_of_indexing(vec![request.clone()]).await; - match pois.len() { - 0 => return Err(anyhow!("no proof of indexing returned {:?}", request)), - 1 => return Ok(pois.into_iter().next().unwrap()), - _ => return Err(anyhow!("multiple proofs of indexing returned")), - } - } - - /// Returns the cached Ethereum calls for the given block hash. - async fn cached_eth_calls( - self: Arc, - network: &str, - block_hash: &[u8], - ) -> anyhow::Result>; - - /// Returns the block cache contents for the given block hash. - async fn block_cache_contents( - self: Arc, - network: &str, - block_hash: &[u8], - ) -> anyhow::Result>; - - /// Returns the entity changes for the given block number. - async fn entity_changes( - self: Arc, - subgraph_id: &str, - block_number: u64, - ) -> anyhow::Result; -} - -impl PartialEq for dyn Indexer { - fn eq(&self, other: &Self) -> bool { - self.id() == other.id() - } -} - -impl Eq for dyn Indexer {} - -impl Hash for dyn Indexer { - fn hash(&self, state: &mut H) { - self.id().hash(state) - } -} - -impl PartialOrd for dyn Indexer { - fn partial_cmp(&self, other: &Self) -> Option { - self.id().partial_cmp(other.id()) - } -} - -impl Ord for dyn Indexer { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.id().cmp(other.id()) - } -} - -/// A wrapper around some inner data `T` that has an associated [`Indexer`]. -pub struct WithIndexer { - pub indexer: Arc, - pub inner: T, -} - -impl WithIndexer { - pub fn new(indexer: Arc, inner: T) -> Self { - Self { indexer, inner } - } -} - -#[derive(Debug)] -pub struct CachedEthereumCall { - pub id_hash: Vec, - pub return_value: Vec, - pub contract_address: Vec, -} - -pub type EntityType = String; -pub type EntityId = String; - -pub struct EntityChanges { - pub updates: HashMap>, - pub deletions: HashMap>, -} diff --git a/backend/crates/common/src/lib.rs b/backend/crates/common/src/lib.rs deleted file mode 100644 index 9a73d86..0000000 --- a/backend/crates/common/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -pub mod block_choice; -pub mod config; -pub mod graphql_api; -mod indexer; -pub mod network_subgraph; -mod prometheus_metrics; -pub mod queries; -pub mod store; -mod types; - -#[cfg(feature = "tests")] -pub mod test_utils; - -pub use prometheus_metrics::{metrics, PrometheusExporter, PrometheusMetrics}; - -pub mod prelude { - pub use super::config::*; - pub use super::indexer::*; - pub use super::queries::{query_indexing_statuses, query_proofs_of_indexing}; - pub use super::store; - pub use super::store::Store; - pub use super::types::*; -} diff --git a/backend/crates/common/src/network_subgraph/mod.rs b/backend/crates/common/src/network_subgraph/mod.rs deleted file mode 100644 index 4b3ddfe..0000000 --- a/backend/crates/common/src/network_subgraph/mod.rs +++ /dev/null @@ -1,350 +0,0 @@ -#![allow(dead_code)] - -use std::collections::BTreeMap; -use std::sync::Arc; -use std::time::Duration; - -use anyhow::anyhow; -use reqwest::Url; -use serde::de::DeserializeOwned; -use serde_derive::{Deserialize, Serialize}; -use tracing::warn; - -use crate::config::{IndexerConfig, IndexerUrls}; -use crate::prelude::{Indexer as IndexerTrait, RealIndexer}; - -#[derive(Debug, Clone)] -pub struct NetworkSubgraph { - endpoint: String, - timeout: Duration, - client: reqwest::Client, -} - -impl NetworkSubgraph { - /// Creates a new [`NetworkSubgraph`] with the given endpoint. - pub fn new(endpoint: String) -> Self { - const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60); - - Self { - endpoint, - timeout: DEFAULT_TIMEOUT, - client: reqwest::Client::new(), - } - } - - /// Sets the timeout for requests to the network subgraph. - pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self { - self.timeout = timeout; - self - } - - pub async fn indexers_by_staked_tokens(&self) -> anyhow::Result>> { - let response_data: GraphqlResponseTopIndexers = self - .graphql_query_no_errors( - queries::INDEXERS_BY_STAKED_TOKENS_QUERY, - vec![], - "error(s) querying top indexers from the network subgraph", - ) - .await?; - - let mut indexers: Vec> = vec![]; - for indexer in response_data.indexers { - let indexer_id = indexer.id.clone(); - let real_indexer = - indexer_allocation_data_to_real_indexer(IndexerAllocation { indexer }); - - match real_indexer { - Ok(indexer) => indexers.push(Arc::new(indexer)), - Err(e) => warn!( - err = %e.to_string(), - indexer_id, - "Received bad indexer for network subgraph query; ignoring", - ), - } - } - - Ok(indexers) - } - - pub async fn indexers_by_allocations(&self) -> anyhow::Result>> { - let sg_deployments = self.subgraph_deployments().await?; - - let mut indexers: Vec> = vec![]; - for deployment in sg_deployments { - for indexer_allocation in deployment.indexer_allocations { - let url = indexer_allocation.indexer.url.clone(); - if let Ok(indexer) = indexer_allocation_data_to_real_indexer(indexer_allocation) { - indexers.push(Arc::new(indexer)); - } else { - warn!(url, "Failed to create indexer from allocation data"); - } - } - } - - Ok(indexers) - } - - pub async fn indexer_by_address( - &self, - address: &[u8], - ) -> anyhow::Result> { - let hex_encoded_addr_json = serde_json::to_value(format!("0x{}", hex::encode(address))) - .expect("Unable to hex encode address"); - let response_data: ResponseData = self - .graphql_query_no_errors( - queries::INDEXER_BY_ADDRESS_QUERY, - vec![("id".to_string(), hex_encoded_addr_json)], - "error(s) querying indexer by address from the network subgraph", - ) - .await?; - - #[derive(Debug, Deserialize)] - #[serde(rename_all = "camelCase")] - struct ResponseData { - indexers: Vec, - } - - #[derive(Debug, Deserialize)] - #[serde(rename_all = "camelCase")] - struct IndexerData { - url: String, - default_display_name: String, - } - - let indexer_data = response_data.indexers.first().ok_or_else(|| { - anyhow::anyhow!("No indexer found for address 0x{}", hex::encode(address)) - })?; - - let indexer = Arc::new(RealIndexer::new(IndexerConfig { - name: indexer_data.default_display_name.clone(), - urls: IndexerUrls { - status: Url::parse(&format!("{}/status", indexer_data.url))?, - }, - })); - - Ok(indexer) - } - - // The `curation_threshold` is denominated in GRT. - pub async fn subgraph_deployments(&self) -> anyhow::Result> { - let response_data: GraphqlResponseSgDeployments = self - .graphql_query_no_errors( - queries::DEPLOYMENTS_QUERY, - vec![], - "error(s) querying deployments from the network subgraph", - ) - .await?; - - Ok(response_data.subgraph_deployments) - } - - async fn graphql_query_no_errors( - &self, - query: impl ToString, - variables: Vec<(String, serde_json::Value)>, - err_msg: &str, - ) -> anyhow::Result { - let response = self.graphql_query(query, variables).await?; - let response_data = response.data.ok_or_else(|| { - anyhow::anyhow!( - "{}: {}", - err_msg, - serde_json::to_string_pretty(&response.errors.unwrap_or_default()) - .expect("Unable to encode query errors") - ) - })?; - - Ok(serde_json::from_value(response_data)?) - } - - async fn graphql_query( - &self, - query: impl ToString, - variables: Vec<(String, serde_json::Value)>, - ) -> anyhow::Result { - let request = GraphqlRequest { - query: query.to_string(), - variables: BTreeMap::from_iter(variables), - }; - - tracing::trace!(timeout = ?self.timeout, endpoint = self.endpoint, "Sending GraphQL request"); - - Ok(self - .client - .post(&self.endpoint) - .json(&request) - .timeout(self.timeout) - .send() - .await? - .error_for_status()? - .json() - .await?) - } -} - -fn indexer_allocation_data_to_real_indexer( - indexer_allocation: IndexerAllocation, -) -> anyhow::Result { - let indexer = indexer_allocation.indexer; - let mut url: Url = indexer - .url - .ok_or_else(|| anyhow!("Indexer without URL"))? - .parse()?; - url.set_path("/status"); - let config = IndexerConfig { - name: indexer.id, - urls: IndexerUrls { status: url }, - }; - Ok(RealIndexer::new(config)) -} - -#[derive(Serialize)] -struct GraphqlRequest { - query: String, - variables: BTreeMap, -} - -#[derive(Deserialize)] -struct GraphqlResponse { - data: Option, - errors: Option>, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct GraphqlResponseSgDeployments { - subgraph_deployments: Vec, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct GraphqlResponseTopIndexers { - indexers: Vec, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct SubgraphDeployment { - pub ipfs_hash: String, - pub indexer_allocations: Vec, -} - -#[derive(Deserialize, Debug)] -pub struct IndexerAllocation { - pub indexer: Indexer, -} - -#[derive(Debug, Deserialize)] -pub struct Indexer { - pub id: String, - pub url: Option, -} - -impl NetworkSubgraph {} - -mod util { - use tiny_cid::Cid; - - pub fn bytes32_to_cid_v0(bytes32: [u8; 32]) -> Cid { - let mut cidv0: [u8; 34] = [0; 34]; - - // The start of any CIDv0. - cidv0[0] = 0x12; - cidv0[1] = 0x20; - - cidv0[2..].copy_from_slice(&bytes32); - - // Unwrap: We've constructed a valid CIDv0. - Cid::read_bytes(cidv0.as_ref()).unwrap() - } - - /// # Panics - /// - /// Panics if `cid` version is not `v0`. - pub fn cid_v0_to_bytes32(cid: &Cid) -> [u8; 32] { - assert!(cid.version() == tiny_cid::Version::V0); - let cid_bytes = cid.to_bytes(); - - // A CIDv0 in byte form is 34 bytes long, starting with 0x1220. - assert_eq!(cid_bytes.len(), 34); - - let mut bytes: [u8; 32] = [0; 32]; - bytes.copy_from_slice(&cid_bytes[2..]); - bytes - } - - #[cfg(test)] - mod tests { - use quickcheck::{Arbitrary, Gen}; - use quickcheck_macros::quickcheck; - - #[derive(Debug, Clone)] - struct Bytes([u8; 32]); - - impl Arbitrary for Bytes { - fn arbitrary(g: &mut Gen) -> Self { - let mut bytes = [0; 32]; - bytes.fill_with(|| Arbitrary::arbitrary(g)); - Self(bytes) - } - } - - #[quickcheck] - fn convert_to_cid_v0_and_back(bytes32: Bytes) { - let cid = super::bytes32_to_cid_v0(bytes32.0); - let bytes32_back = super::cid_v0_to_bytes32(&cid); - assert_eq!(bytes32.0, bytes32_back); - } - } -} - -mod queries { - pub const INDEXERS_BY_STAKED_TOKENS_QUERY: &str = - include_str!("queries/indexers_by_staked_tokens.graphql"); - pub const DEPLOYMENTS_QUERY: &str = include_str!("queries/deployments.graphql"); - pub const INDEXER_BY_ADDRESS_QUERY: &str = include_str!("queries/indexer_by_address.graphql"); -} - -#[cfg(test)] -mod tests { - use super::*; - - fn mainnet_network_subgraph() -> NetworkSubgraph { - NetworkSubgraph::new( - "https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet" - .to_string(), - ) - } - - #[tokio::test] - async fn mainnet_indexers_by_staked_tokens_no_panic() { - let network_sg = mainnet_network_subgraph(); - let indexers = network_sg.indexers_by_staked_tokens().await.unwrap(); - assert!(indexers.len() > 0); - } - - #[tokio::test] - async fn mainnet_indexers_by_allocations_no_panic() { - let network_sg = mainnet_network_subgraph(); - let indexers = network_sg.indexers_by_allocations().await.unwrap(); - assert!(indexers.len() > 0); - } - - #[tokio::test] - async fn mainnet_deployments_no_panic() { - let network_sg = mainnet_network_subgraph(); - let deployments = network_sg.subgraph_deployments().await.unwrap(); - assert!(deployments.len() > 0); - } - - #[tokio::test] - #[should_panic] // FIXME - async fn mainnet_fetch_indexer() { - let network_sg = mainnet_network_subgraph(); - // ellipfra.eth: - // https://thegraph.com/explorer/profile/0x62a0bd1d110ff4e5b793119e95fc07c9d1fc8c4a?view=Indexing&chain=mainnet - let addr = hex::decode("62a0bd1d110ff4e5b793119e95fc07c9d1fc8c4a").unwrap(); - let indexer = network_sg.indexer_by_address(&addr).await.unwrap(); - assert_eq!(indexer.address(), Some(&addr[..])); - } -} diff --git a/backend/crates/common/src/network_subgraph/queries/deployments.graphql b/backend/crates/common/src/network_subgraph/queries/deployments.graphql deleted file mode 100644 index 71a006f..0000000 --- a/backend/crates/common/src/network_subgraph/queries/deployments.graphql +++ /dev/null @@ -1,14 +0,0 @@ -{ - subgraphDeployments( - where: { indexerAllocations_: { status_in: [Active] } } - orderBy: stakedTokens - ) { - ipfsHash - indexerAllocations(orderBy: allocatedTokens) { - indexer { - id - url - } - } - } -} diff --git a/backend/crates/common/src/store/schema.rs b/backend/crates/common/src/store/schema.rs deleted file mode 100644 index 5a9dad0..0000000 --- a/backend/crates/common/src/store/schema.rs +++ /dev/null @@ -1,104 +0,0 @@ -// @generated automatically by Diesel CLI. - -diesel::table! { - blocks (id) { - id -> Int8, - network_id -> Int4, - number -> Int8, - hash -> Bytea, - } -} - -diesel::table! { - divergence_investigation_reports (uuid) { - uuid -> Text, - report -> Jsonb, - created_at -> Timestamp, - } -} - -diesel::table! { - indexers (id) { - id -> Int4, - name -> Nullable, - address -> Nullable, - created_at -> Timestamp, - } -} - -diesel::table! { - live_pois (id) { - id -> Int4, - sg_deployment_id -> Int4, - indexer_id -> Int4, - poi_id -> Int4, - } -} - -diesel::table! { - networks (id) { - id -> Int4, - name -> Text, - created_at -> Timestamp, - caip2 -> Nullable, - } -} - -diesel::table! { - pending_divergence_investigation_requests (uuid) { - uuid -> Text, - request -> Jsonb, - created_at -> Timestamp, - } -} - -diesel::table! { - pois (id) { - id -> Int4, - poi -> Bytea, - sg_deployment_id -> Int4, - indexer_id -> Int4, - block_id -> Int8, - created_at -> Timestamp, - } -} - -diesel::table! { - sg_deployments (id) { - id -> Int4, - ipfs_cid -> Text, - network -> Int4, - created_at -> Timestamp, - } -} - -diesel::table! { - sg_names (id) { - id -> Int4, - sg_deployment_id -> Int4, - name -> Text, - created_at -> Timestamp, - } -} - -diesel::joinable!(blocks -> networks (network_id)); -diesel::joinable!(live_pois -> indexers (indexer_id)); -diesel::joinable!(live_pois -> pois (poi_id)); -diesel::joinable!(live_pois -> sg_deployments (sg_deployment_id)); -diesel::joinable!(pois -> blocks (block_id)); -diesel::joinable!(pois -> indexers (indexer_id)); -diesel::joinable!(pois -> sg_deployments (sg_deployment_id)); -diesel::joinable!(sg_deployments -> networks (network)); -diesel::joinable!(sg_names -> sg_deployments (sg_deployment_id)); - -diesel::allow_tables_to_appear_in_same_query!( - blocks, - divergence_investigation_reports, - indexers, - live_pois, - networks, - pending_divergence_investigation_requests, - pois, - sg_deployments, - sg_names, -); diff --git a/backend/crates/common/src/test_utils/mod.rs b/backend/crates/common/src/test_utils/mod.rs deleted file mode 100644 index 363e620..0000000 --- a/backend/crates/common/src/test_utils/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -pub mod gen; -pub mod mocks; - -use std::env; - -use once_cell::sync::Lazy; -use rand::rngs::{OsRng, SmallRng}; -use rand::{RngCore, SeedableRng}; - -pub static TEST_SEED: Lazy = Lazy::new(|| { - let seed = env::var("TEST_SEED") - .map(|seed| seed.parse().expect("Invalid TEST_SEED value")) - .unwrap_or(OsRng.next_u64()); - - println!("------------------------------------------------------------------------"); - println!("TEST_SEED={}", seed); - println!(" This value can be changed via the environment variable TEST_SEED."); - println!("------------------------------------------------------------------------"); - - seed -}); - -pub fn fast_rng(seed_extra: u64) -> SmallRng { - SmallRng::seed_from_u64(*TEST_SEED + seed_extra) -} diff --git a/backend/crates/common/src/types.rs b/backend/crates/common/src/types.rs deleted file mode 100644 index e2dbcfc..0000000 --- a/backend/crates/common/src/types.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::fmt; -use std::ops::Deref; -use std::sync::Arc; - -use serde::Serialize; - -use crate::indexer::Indexer; -use crate::store::models::WritablePoi; - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Ord, PartialOrd)] -pub struct BlockPointer { - pub number: u64, - pub hash: Option, -} - -impl fmt::Display for BlockPointer { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "#{} ({})", - self.number, - self.hash - .as_ref() - .map_or("no hash".to_string(), |hash| format!("{}", hash)) - ) - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub struct SubgraphDeployment(pub String); - -impl Deref for SubgraphDeployment { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[derive(Debug, Clone, Eq)] -pub struct IndexingStatus { - pub indexer: Arc, - pub deployment: SubgraphDeployment, - pub network: String, - pub latest_block: BlockPointer, - pub earliest_block_num: u64, -} - -impl PartialEq for IndexingStatus { - fn eq(&self, other: &Self) -> bool { - &*self.indexer == &*other.indexer - && self.deployment == other.deployment - && self.network == other.network - && self.latest_block == other.latest_block - } -} - -/// A 32-byte array that can be easily converted to and from hex strings. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Ord, PartialOrd)] -pub struct Bytes32(pub [u8; 32]); - -impl TryFrom<&str> for Bytes32 { - type Error = anyhow::Error; - - fn try_from(s: &str) -> Result { - Ok(Self(hex::FromHex::from_hex(s.trim_start_matches("0x"))?)) - } -} - -impl TryFrom> for Bytes32 { - type Error = anyhow::Error; - - fn try_from(v: Vec) -> Result { - if v.len() != 32 { - return Err(anyhow::anyhow!("Expected 32 bytes, got {}", v.len())); - } - let mut bytes = [0u8; 32]; - bytes.copy_from_slice(&v); - Ok(Self(bytes)) - } -} - -impl fmt::Display for Bytes32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", hex::encode(&self.0)) - } -} - -#[derive(Debug, Clone, Eq, PartialOrd, Ord)] -pub struct ProofOfIndexing { - pub indexer: Arc, - pub deployment: SubgraphDeployment, - pub block: BlockPointer, - pub proof_of_indexing: Bytes32, -} - -impl PartialEq for ProofOfIndexing { - fn eq(&self, other: &Self) -> bool { - &*self.indexer == &*other.indexer - && self.deployment == other.deployment - && self.block == other.block - && self.proof_of_indexing == other.proof_of_indexing - } -} - -impl WritablePoi for ProofOfIndexing { - fn deployment_cid(&self) -> &str { - self.deployment.as_str() - } - - fn indexer_id(&self) -> &str { - self.indexer.id() - } - - fn indexer_address(&self) -> Option<&[u8]> { - self.indexer.address().map(AsRef::as_ref) - } - - fn block(&self) -> BlockPointer { - self.block.clone() - } - - fn proof_of_indexing(&self) -> &[u8] { - &self.proof_of_indexing.0 - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] -pub struct DivergingBlock { - pub block: BlockPointer, - pub proof_of_indexing1: Bytes32, - pub proof_of_indexing2: Bytes32, -} - -#[derive(Clone, Debug)] -pub struct POICrossCheckReport { - pub poi1: ProofOfIndexing, - pub poi2: ProofOfIndexing, - pub diverging_block: Option, -} - -#[derive(Debug, Clone)] -pub struct PoiRequest { - pub deployment: SubgraphDeployment, - pub block_number: u64, -} diff --git a/backend/crates/common/tests/it_indexing_statuses.rs b/backend/crates/common/tests/it_indexing_statuses.rs deleted file mode 100644 index 8b5e2c9..0000000 --- a/backend/crates/common/tests/it_indexing_statuses.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::sync::Arc; -use std::time::Duration; - -use reqwest::Url; - -use graphix_common::config::{IndexerConfig, IndexerUrls}; -use graphix_common::prelude::{Indexer, RealIndexer, SubgraphDeployment}; - -/// Test utility function to create a valid `Indexer` from an arbitrary base url. -fn test_indexer_from_url(url: impl Into) -> Arc { - let url: Url = url.into().parse().expect("Invalid status url"); - let conf = IndexerConfig { - name: url.host().unwrap().to_string(), - urls: IndexerUrls { - status: url.join("status").unwrap(), - }, - }; - Arc::new(RealIndexer::new(conf)) -} - -/// Test utility function to create a valid `SubgraphDeployment` with an arbitrary deployment -/// id/ipfs hash. -fn test_deployment_id(deployment: impl Into) -> SubgraphDeployment { - SubgraphDeployment(deployment.into()) -} - -#[tokio::test] -async fn send_indexer_statuses_query() { - //// Given - let indexer = test_indexer_from_url("https://testnet-indexer-03-europe-cent.thegraph.com"); - - let test_deployment = test_deployment_id("QmeYTH2fK2wv96XvnCGH2eyKFE8kmRfo53zYVy5dKysZtH"); - - //// When - let request_fut = Indexer::indexing_statuses(indexer); - let response = tokio::time::timeout(Duration::from_secs(10), request_fut) - .await - .expect("Timeout"); - - //// Then - assert!(response.is_ok()); - - let response = response.unwrap(); - assert!(!response.is_empty()); - assert!(response - .iter() - .any(|status| status.deployment == test_deployment)); -} diff --git a/backend/crates/cross-checker/src/main.rs b/backend/crates/cross-checker/src/main.rs deleted file mode 100644 index 4d78b97..0000000 --- a/backend/crates/cross-checker/src/main.rs +++ /dev/null @@ -1,106 +0,0 @@ -mod bisect; -mod utils; - -use std::collections::HashSet; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::Duration; - -use clap::Parser; -use graphix_common::prelude::{Config, Indexer}; -use graphix_common::queries::{query_indexing_statuses, query_proofs_of_indexing}; -use graphix_common::{config, metrics, store, PrometheusExporter}; -use prometheus_exporter::prometheus; -use tokio::sync::watch; -use tracing::*; -use tracing_subscriber; - -use crate::bisect::handle_divergence_investigation_requests; - -#[derive(Parser, Debug)] -struct CliOptions { - #[clap(long)] - config: PathBuf, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - init_tracing(); - - info!("Parse options"); - let cli_options = CliOptions::parse(); - - info!("Loading configuration file"); - let config = Config::read(&cli_options.config)?; - - info!("Initialize store and running migrations"); - let store = store::Store::new(&config.database_url).await?; - info!("Store initialization successful"); - - let sleep_duration = Duration::from_secs(config.polling_period_in_seconds); - - // Prometheus metrics. - let registry = prometheus::default_registry().clone(); - let _exporter = PrometheusExporter::start(config.prometheus_port, registry.clone()).unwrap(); - - info!("Initializing bisect request handler"); - let store_clone = store.clone(); - let (tx_indexers, rx_indexers) = watch::channel(vec![]); - tokio::spawn(async move { - handle_divergence_investigation_requests(&store_clone, rx_indexers) - .await - .unwrap() - }); - - loop { - info!("New main loop iteration"); - info!("Initialize inputs (indexers, indexing statuses etc.)"); - - let mut indexers = config::config_to_indexers(config.clone()).await?; - // Different data sources, especially network subgraphs, result in - // duplicate indexers. - indexers = deduplicate_indexers(&indexers); - - tx_indexers.send(indexers.clone())?; - - let indexing_statuses = query_indexing_statuses(indexers, metrics()).await; - - info!("Monitor proofs of indexing"); - let pois = query_proofs_of_indexing(indexing_statuses, config.block_choice_policy).await; - - info!(pois = pois.len(), "Finished tracking Pois"); - - let write_err = store.write_pois(&pois, store::PoiLiveness::Live).err(); - if let Some(err) = write_err { - error!(error = %err, "Failed to write POIs to database"); - } - - info!( - sleep_seconds = sleep_duration.as_secs(), - "Sleeping for a while before next main loop iteration" - ); - tokio::time::sleep(sleep_duration).await; - } -} - -fn init_tracing() { - tracing_subscriber::fmt::init(); -} - -fn deduplicate_indexers(indexers: &[Arc]) -> Vec> { - info!(len = indexers.len(), "Deduplicating indexers"); - let mut seen = HashSet::new(); - let mut deduplicated = vec![]; - for indexer in indexers { - if !seen.contains(indexer.id()) { - deduplicated.push(indexer.clone()); - seen.insert(indexer.id().to_string()); - } - } - info!( - len = deduplicated.len(), - delta = indexers.len() - deduplicated.len(), - "Successfully deduplicated indexers" - ); - deduplicated -} diff --git a/backend/crates/schema/Cargo.toml b/backend/crates/schema/Cargo.toml deleted file mode 100644 index 10d5480..0000000 --- a/backend/crates/schema/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "graphix-schema" -version = "0.1.0" -edition = "2021" - -[dependencies] - -[build-dependencies] -async-graphql = "5" -graphix-common = { path = "../common" } diff --git a/crates/autogen_graphql_schema/Cargo.toml b/crates/autogen_graphql_schema/Cargo.toml new file mode 100644 index 0000000..b635731 --- /dev/null +++ b/crates/autogen_graphql_schema/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "graphix-autogen-graphql-schema" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +async-graphql = "7" +graphix_lib = { path = "../graphix-lib" } diff --git a/backend/crates/schema/build.rs b/crates/autogen_graphql_schema/build.rs similarity index 71% rename from backend/crates/schema/build.rs rename to crates/autogen_graphql_schema/build.rs index b218046..93bc0b5 100644 --- a/backend/crates/schema/build.rs +++ b/crates/autogen_graphql_schema/build.rs @@ -2,8 +2,7 @@ use std::env; use std::fs::File; use std::io::*; -use async_graphql::{EmptySubscription, Schema}; -use graphix_common::graphql_api::{MutationRoot, QueryRoot}; +use graphix_lib::graphql_api::api_schema_builder; fn main() -> std::io::Result<()> { // We're only interested in re-generating the API schema if build @@ -11,13 +10,12 @@ fn main() -> std::io::Result<()> { // See . println!("cargo:rerun-if-changed=build.rs"); - let api_schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription).finish(); let path = env::current_dir()?.join("graphql/api_schema.graphql"); let mut f = File::create(&path)?; f.write_all(b"# AUTOGENERATED. DO NOT MODIFY. ALL CHANGES WILL BE LOST.\n\n")?; - f.write_all(api_schema.sdl().as_bytes())?; + f.write_all(api_schema_builder().finish().sdl().as_bytes())?; println!("Updated: {}", path.display()); Ok(()) diff --git a/backend/crates/schema/graphql/api_schema.graphql b/crates/autogen_graphql_schema/graphql/api_schema.graphql similarity index 96% rename from backend/crates/schema/graphql/api_schema.graphql rename to crates/autogen_graphql_schema/graphql/api_schema.graphql index 2e3d5ee..1ca470c 100644 --- a/backend/crates/schema/graphql/api_schema.graphql +++ b/crates/autogen_graphql_schema/graphql/api_schema.graphql @@ -186,17 +186,21 @@ enum DivergenceInvestigationStatus { An indexer that is known to Graphix. """ type Indexer { - """ - The indexer's ID, which is equal to its hex-encoded address with a '0x' - prefix. - """ id: String! + name: String + address: String + version: IndexerVersion """ The number of tokens allocated to the indexer, if known. """ allocatedTokens: Int } +type IndexerVersion { + version: String! + commit: String! +} + """ A filter for indexers. """ @@ -373,6 +377,7 @@ type QueryRoot { liveProofsOfIndexing(filter: PoisQuery!): [ProofOfIndexing!]! poiAgreementRatios(indexerName: String!): [PoiAgreementRatio!]! divergenceInvestigationReport(uuid: String!): DivergenceInvestigationReport + networks: [Network!]! } """ @@ -398,6 +403,8 @@ input SgDeploymentsQuery { } +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT schema { query: QueryRoot mutation: MutationRoot diff --git a/backend/crates/schema/src/lib.rs b/crates/autogen_graphql_schema/src/lib.rs similarity index 100% rename from backend/crates/schema/src/lib.rs rename to crates/autogen_graphql_schema/src/lib.rs diff --git a/crates/common-types/Cargo.toml b/crates/common-types/Cargo.toml new file mode 100644 index 0000000..c27b95d --- /dev/null +++ b/crates/common-types/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "graphix_common_types" +version = "0.1.0" +edition = "2021" + +[dependencies] +async-graphql = "7" +diesel = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" diff --git a/backend/crates/common/src/graphql_api/types.rs b/crates/common-types/src/lib.rs similarity index 91% rename from backend/crates/common/src/graphql_api/types.rs rename to crates/common-types/src/lib.rs index 48ca2aa..97476f2 100644 --- a/backend/crates/common/src/graphql_api/types.rs +++ b/crates/common-types/src/lib.rs @@ -7,8 +7,6 @@ use async_graphql::*; use diesel::deserialize::FromSqlRow; use serde::{Deserialize, Serialize}; -use crate::store::models::{self}; - type HexBytesWith0xPrefix = String; type UuidString = String; @@ -263,41 +261,18 @@ pub struct ProofOfIndexing { /// An indexer that is known to Graphix. #[derive(SimpleObject, Debug)] pub struct Indexer { - /// The indexer's ID, which is equal to its hex-encoded address with a '0x' - /// prefix. - pub id: HexBytesWith0xPrefix, + pub id: String, + pub name: Option, + pub address: Option, + pub version: Option, /// The number of tokens allocated to the indexer, if known. pub allocated_tokens: Option, } -impl From for Indexer { - fn from(indexer: models::Indexer) -> Self { - Self { - id: indexer.name.unwrap_or_default(), - allocated_tokens: None, // TODO: we don't store this in the db yet - } - } -} - -impl From for ProofOfIndexing { - fn from(poi: models::Poi) -> Self { - Self { - allocated_tokens: None, - deployment: Deployment { - id: poi.sg_deployment.cid.clone(), - }, - hash: poi.poi_hex(), - block: Block { - network: Network { - name: "mainnet".to_string(), - caip2: None, - }, - number: poi.block.number as u64, - hash: hex::encode(poi.block.hash), - }, - indexer: Indexer::from(models::Indexer::from(poi.indexer)), - } - } +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Ord, PartialOrd, SimpleObject)] +pub struct IndexerVersion { + pub version: String, + pub commit: String, } #[derive(InputObject)] @@ -309,10 +284,10 @@ struct POICrossCheckReportRequest { } #[derive(SimpleObject)] -struct DivergingBlock { - block: PartialBlock, - proof_of_indexing1: String, - proof_of_indexing2: String, +pub struct DivergingBlock { + pub block: PartialBlock, + pub proof_of_indexing1: String, + pub proof_of_indexing2: String, } #[derive(SimpleObject)] diff --git a/crates/graphix-lib/Cargo.toml b/crates/graphix-lib/Cargo.toml new file mode 100644 index 0000000..16ad997 --- /dev/null +++ b/crates/graphix-lib/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "graphix_lib" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +async-graphql = "7" +async-trait = { version = "0.1", optional = true } +diesel = "2" +futures = "0.3" +graphix_common_types = { path = "../common-types" } +graphix_indexer_client = { path = "../indexer-client" } +graphix_network_sg_client = { path = "../network-sg-client" } +graphix_store = { path = "../store" } +hex = "0.4" +once_cell = { version = "1", optional = true } +prometheus = { version = "0.13", optional = true } +prometheus_exporter = "0.8" +rand = { version = "0.8", optional = true } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +serde_yaml = "0.9" +tracing = "0.1" +url = "2.5" + +[build-dependencies] +reqwest = { version = "0.11", features = ["blocking"] } + +[features] +tests = ["async-trait", "once_cell", "prometheus", "rand"] + +[dev-dependencies] +graphix_common_types = { path = "../common-types" } +graphix_lib = { path = ".", features = ["tests"] } +itertools = "0.12" +once_cell = { version = "1.9.0" } +prometheus = "0.13" +quickcheck = "1" +quickcheck_macros = "1" +rand = { version = "0.8.4", features = ["small_rng"] } +reqwest = { version = "0.11" } +tokio = { version = "1", features = ["macros"] } diff --git a/backend/crates/common/src/block_choice.rs b/crates/graphix-lib/src/block_choice.rs similarity index 91% rename from backend/crates/common/src/block_choice.rs rename to crates/graphix-lib/src/block_choice.rs index bbc49ce..a26d405 100644 --- a/backend/crates/common/src/block_choice.rs +++ b/crates/graphix-lib/src/block_choice.rs @@ -1,22 +1,16 @@ +use graphix_indexer_client::IndexingStatus; use serde::Deserialize; -use crate::prelude::IndexingStatus; - -#[derive(Copy, Clone, Debug, Deserialize)] +#[derive(Copy, Clone, Debug, Default, Deserialize)] #[serde(rename_all = "camelCase")] pub enum BlockChoicePolicy { // Use the earliest block that all indexers have in common Earliest, // Use the block that maximizes the total number of blocks synced across all indexers + #[default] MaxSyncedBlocks, } -impl Default for BlockChoicePolicy { - fn default() -> Self { - BlockChoicePolicy::MaxSyncedBlocks - } -} - impl BlockChoicePolicy { pub fn choose_block<'a>( &self, diff --git a/backend/crates/common/src/config.rs b/crates/graphix-lib/src/config.rs similarity index 72% rename from backend/crates/common/src/config.rs rename to crates/graphix-lib/src/config.rs index d57935d..9179ed9 100644 --- a/backend/crates/common/src/config.rs +++ b/crates/graphix-lib/src/config.rs @@ -1,26 +1,39 @@ +use std::borrow::Cow; use std::fs::File; use std::path::Path; use std::sync::Arc; -use reqwest::Url; +use anyhow::Context; +use graphix_indexer_client::{HexString, Indexer, IndexerId, IndexerInterceptor, RealIndexer}; +use graphix_network_sg_client::NetworkSubgraphClient; use serde::{Deserialize, Deserializer}; use tracing::{info, warn}; +use url::Url; use crate::block_choice::BlockChoicePolicy; -use crate::indexer::{Indexer, IndexerInterceptor, RealIndexer}; -use crate::network_subgraph::NetworkSubgraph; +use crate::PrometheusMetrics; /// A [`serde`]-compatible representation of Graphix's YAML configuration file. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Config { - pub database_url: String, + #[serde(default = "Config::default_true")] + pub enable_indexing: bool, + #[serde(default = "Config::default_true")] + pub enable_api: bool, + #[serde(default = "Config::default_prometheus_port")] pub prometheus_port: u16, + #[serde(default = "Config::default_api_port")] + pub api_port: u16, + + pub database_url: String, + + // Indexing options + // ---------------- pub sources: Vec, #[serde(default)] pub block_choice_policy: BlockChoicePolicy, - #[serde(default = "Config::default_polling_period_in_seconds")] pub polling_period_in_seconds: u64, } @@ -28,8 +41,7 @@ pub struct Config { impl Config { pub fn read(path: &Path) -> anyhow::Result { let file = File::open(path)?; - Ok(serde_yaml::from_reader(file) - .map_err(|e| anyhow::Error::new(e).context("invalid config file"))?) + serde_yaml::from_reader(file).context("invalid config file") } pub fn indexers(&self) -> Vec { @@ -83,20 +95,44 @@ impl Config { fn default_prometheus_port() -> u16 { 9184 } -} -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd)] -#[serde(rename_all = "camelCase")] -pub struct IndexerUrls { - #[serde(deserialize_with = "deserialize_url")] - pub status: Url, + fn default_api_port() -> u16 { + 3030 + } + + fn default_true() -> bool { + true + } } #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct IndexerConfig { - pub name: String, - pub urls: IndexerUrls, + pub name: Option, + pub address: Vec, + #[serde(deserialize_with = "deserialize_url")] + pub index_node_endpoint: Url, +} + +fn deserialize_url<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + Url::parse(&s).map_err(serde::de::Error::custom) +} + +impl IndexerId for IndexerConfig { + fn address(&self) -> &[u8] { + self.address.as_slice() + } + + fn name(&self) -> Option> { + match &self.name { + Some(name) => Some(Cow::Borrowed(name)), + None => None, + } + } } #[derive(Clone, Debug, Deserialize)] @@ -118,24 +154,19 @@ pub struct NetworkSubgraphConfig { pub limit: Option, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize)] #[serde(rename_all = "camelCase")] pub enum NetworkSubgraphQuery { + #[default] ByAllocations, ByStakedTokens, } -impl Default for NetworkSubgraphQuery { - fn default() -> Self { - NetworkSubgraphQuery::ByAllocations - } -} - #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InterceptorConfig { pub name: String, - pub target: String, + pub target: HexString>, pub poi_byte: u8, } @@ -148,41 +179,47 @@ pub enum ConfigSource { NetworkSubgraph(NetworkSubgraphConfig), } -fn deserialize_url<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - Url::parse(&s).map_err(serde::de::Error::custom) -} - fn deserialize_hexstring<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; - if !s.starts_with("0x") { - return Err(serde::de::Error::custom("hexstring must start with 0x")); + if let Some(s) = s.strip_prefix("0x") { + hex::decode(s).map_err(serde::de::Error::custom) + } else { + Err(serde::de::Error::custom("missing 0x prefix")) } - hex::decode(&s[2..]).map_err(serde::de::Error::custom) } -pub async fn config_to_indexers(config: Config) -> anyhow::Result>> { +pub async fn config_to_indexers( + config: Config, + metrics: &PrometheusMetrics, +) -> anyhow::Result>> { let mut indexers: Vec> = vec![]; // First, configure all the real, static indexers. for config in config.indexers() { - info!(indexer_id = %config.name, "Configuring indexer"); - indexers.push(Arc::new(RealIndexer::new(config.clone()))); + info!(indexer_address = %config.address_string(), "Configuring indexer"); + indexers.push(Arc::new(RealIndexer::new( + config.name().map(|s| s.into_owned()), + config.address().to_vec(), + config.index_node_endpoint.to_string(), + metrics.public_proofs_of_indexing_requests.clone(), + ))); } // Then, configure the network subgraphs, if required, resulting in "dynamic" // indexers. for config in config.network_subgraphs() { info!(endpoint = %config.endpoint, "Configuring network subgraph"); - let network_subgraph = NetworkSubgraph::new(config.endpoint.clone()); + let network_subgraph = NetworkSubgraphClient::new( + config.endpoint.as_str().parse()?, + metrics.public_proofs_of_indexing_requests.clone(), + ); let network_subgraph_indexers_res = match config.query { - NetworkSubgraphQuery::ByAllocations => network_subgraph.indexers_by_allocations().await, + NetworkSubgraphQuery::ByAllocations => { + network_subgraph.indexers_by_allocations(config.limit).await + } NetworkSubgraphQuery::ByStakedTokens => { network_subgraph.indexers_by_staked_tokens().await } @@ -213,13 +250,14 @@ pub async fn config_to_indexers(config: Config) -> anyhow::Result anyhow::Result; + +pub struct ApiSchemaContext { + pub store: Store, +} + +pub fn api_schema_builder() -> SchemaBuilder { + Schema::build(QueryRoot, MutationRoot, EmptySubscription) +} + +pub fn api_schema(ctx: ApiSchemaContext) -> ApiSchema { + api_schema_builder().data(ctx).finish() +} diff --git a/backend/crates/common/src/graphql_api/server.rs b/crates/graphix-lib/src/graphql_api/server.rs similarity index 86% rename from backend/crates/common/src/graphql_api/server.rs rename to crates/graphix-lib/src/graphql_api/server.rs index d1e1543..9d77224 100644 --- a/backend/crates/common/src/graphql_api/server.rs +++ b/crates/graphix-lib/src/graphql_api/server.rs @@ -1,11 +1,11 @@ use std::collections::BTreeMap; use anyhow::Context as _; -use async_graphql::{Context, EmptySubscription, Object, Result, Schema}; - -use super::types::*; -use crate::store::models::QueriedSgDeployment; -use crate::store::Store; +use async_graphql::{Context, Object, Result}; +use graphix_common_types as types; +use graphix_common_types::*; +use graphix_store::models::QueriedSgDeployment; +use graphix_store::Store; pub struct QueryRoot; @@ -24,11 +24,15 @@ impl QueryRoot { /// Fetches all tracked indexers in this Graphix instance and filters them /// according to some filtering rules. - async fn indexers(&self, ctx: &Context<'_>, filter: IndexersQuery) -> Result> { + async fn indexers( + &self, + ctx: &Context<'_>, + filter: IndexersQuery, + ) -> Result> { let api_ctx = ctx.data::()?; let indexers = api_ctx.store.indexers(filter)?; - Ok(indexers.into_iter().map(Indexer::from).collect()) + Ok(indexers.into_iter().map(Into::into).collect()) } /// Filters through all PoIs ever collected by this Graphix @@ -37,13 +41,13 @@ impl QueryRoot { &self, ctx: &Context<'_>, filter: PoisQuery, - ) -> Result> { + ) -> Result> { let api_ctx = ctx.data::()?; let pois = api_ctx .store .pois(&filter.deployments, filter.block_range, filter.limit)?; - Ok(pois.into_iter().map(ProofOfIndexing::from).collect()) + Ok(pois.into_iter().map(types::ProofOfIndexing::from).collect()) } /// Same as [`QueryRoot::proofs_of_indexing`], but only returns PoIs that @@ -53,7 +57,7 @@ impl QueryRoot { &self, ctx: &Context<'_>, filter: PoisQuery, - ) -> Result> { + ) -> Result> { let api_ctx = ctx.data::()?; let pois = api_ctx.store.live_pois( None, @@ -62,7 +66,7 @@ impl QueryRoot { filter.limit, )?; - Ok(pois.into_iter().map(ProofOfIndexing::from).collect()) + Ok(pois.into_iter().map(Into::into).collect()) } async fn poi_agreement_ratios( @@ -89,9 +93,9 @@ impl QueryRoot { .live_pois(None, Some(&deployment_cids), None, None)?; // Convert POIs to ProofOfIndexing and group by deployment - let mut deployment_to_pois: BTreeMap> = BTreeMap::new(); + let mut deployment_to_pois: BTreeMap> = BTreeMap::new(); for poi in all_deployment_pois { - let proof_of_indexing: ProofOfIndexing = poi.into(); + let proof_of_indexing: types::ProofOfIndexing = poi.into(); deployment_to_pois .entry(proof_of_indexing.deployment.id.clone()) .or_default() @@ -101,7 +105,7 @@ impl QueryRoot { let mut agreement_ratios: Vec = Vec::new(); for poi in indexer_pois { - let poi: ProofOfIndexing = poi.into(); + let poi: types::ProofOfIndexing = poi.into(); let deployment = Deployment { id: poi.deployment.id.clone(), @@ -143,7 +147,7 @@ impl QueryRoot { let ratio = PoiAgreementRatio { poi: poi.hash.clone(), deployment, - block: block, + block, total_indexers, n_agreeing_indexers, n_disagreeing_indexers, @@ -183,6 +187,13 @@ impl QueryRoot { Ok(None) } } + + async fn networks(&self, ctx: &Context<'_>) -> Result> { + let api_ctx = ctx.data::()?; + + let networks = api_ctx.store.networks()?; + Ok(networks) + } } pub struct MutationRoot; @@ -236,14 +247,6 @@ impl MutationRoot { } } -pub type ApiSchema = Schema; - pub struct ApiSchemaContext { pub store: Store, } - -pub fn api_schema(ctx: ApiSchemaContext) -> ApiSchema { - Schema::build(QueryRoot, MutationRoot, EmptySubscription) - .data(ctx) - .finish() -} diff --git a/crates/graphix-lib/src/lib.rs b/crates/graphix-lib/src/lib.rs new file mode 100644 index 0000000..b549a43 --- /dev/null +++ b/crates/graphix-lib/src/lib.rs @@ -0,0 +1,10 @@ +pub mod block_choice; +pub mod config; +pub mod graphql_api; +mod prometheus_metrics; +pub mod queries; + +#[cfg(feature = "tests")] +pub mod test_utils; + +pub use prometheus_metrics::{metrics, PrometheusExporter, PrometheusMetrics}; diff --git a/backend/crates/common/src/prometheus_metrics.rs b/crates/graphix-lib/src/prometheus_metrics.rs similarity index 100% rename from backend/crates/common/src/prometheus_metrics.rs rename to crates/graphix-lib/src/prometheus_metrics.rs diff --git a/backend/crates/common/src/queries.rs b/crates/graphix-lib/src/queries.rs similarity index 70% rename from backend/crates/common/src/queries.rs rename to crates/graphix-lib/src/queries.rs index acaf42d..2a790cd 100644 --- a/backend/crates/common/src/queries.rs +++ b/crates/graphix-lib/src/queries.rs @@ -3,11 +3,13 @@ use std::sync::Arc; use futures::stream::FuturesUnordered; use futures::StreamExt; +use graphix_common_types::IndexerVersion; +use graphix_indexer_client::{ + Indexer, IndexerId, IndexingStatus, PoiRequest, ProofOfIndexing, SubgraphDeployment, +}; use tracing::*; use crate::block_choice::BlockChoicePolicy; -use crate::indexer::Indexer; -use crate::prelude::{IndexingStatus, PoiRequest, ProofOfIndexing, SubgraphDeployment}; use crate::PrometheusMetrics; /// Queries all `indexingStatuses` for all the given indexers. @@ -37,12 +39,12 @@ pub async fn query_indexing_statuses( query_successes += 1; metrics .indexing_statuses_requests - .get_metric_with_label_values(&[indexer.id(), "1"]) + .get_metric_with_label_values(&[&indexer.address_string(), "1"]) .unwrap() .inc(); debug!( - indexer_id = %indexer.id(), + indexer_id = %indexer.address_string(), statuses = %statuses.len(), "Successfully queried indexing statuses" ); @@ -53,12 +55,12 @@ pub async fn query_indexing_statuses( query_failures += 1; metrics .indexing_statuses_requests - .get_metric_with_label_values(&[indexer.id(), "0"]) + .get_metric_with_label_values(&[&indexer.address_string(), "0"]) .unwrap() .inc(); - warn!( - indexer_id = %indexer.id(), + debug!( + indexer_id = %indexer.address_string(), %error, "Failed to query indexing statuses" ); @@ -79,6 +81,51 @@ pub async fn query_indexing_statuses( indexing_statuses } +/// Queries all `indexers` for their `graph-node` versions. +pub async fn query_graph_node_versions( + indexers: &[Arc], + _metrics: &PrometheusMetrics, +) -> HashMap, anyhow::Result> { + let span = span!(Level::TRACE, "query_graph_node_versions"); + let _enter_span = span.enter(); + + info!("Querying graph-node versions..."); + + let mut futures = FuturesUnordered::new(); + for indexer in indexers { + futures.push(async move { (indexer.clone(), indexer.clone().version().await) }); + } + + let mut versions = HashMap::new(); + while let Some((indexer, version_result)) = futures.next().await { + match &version_result { + Ok(version) => { + trace!( + indexer_id = %indexer.address_string(), + version = %version.version, + commit = %version.commit, + "Successfully queried graph-node version" + ); + } + Err(error) => { + trace!( + indexer_id = %indexer.address_string(), + %error, + "Failed to query graph-node version" + ); + } + } + versions.insert(indexer, version_result); + } + + info!( + indexers = versions.len(), + "Finished querying graph-node versions for all indexers" + ); + + versions +} + pub async fn query_proofs_of_indexing( indexing_statuses: Vec, block_choice_policy: BlockChoicePolicy, @@ -119,13 +166,13 @@ pub async fn query_proofs_of_indexing( ( deployment.clone(), statuses_by_deployment.get(deployment).and_then(|statuses| { - block_choice_policy.choose_block(statuses.iter().map(|&s| s)) + block_choice_policy.choose_block(statuses.iter().copied()) }), ) })); // Fetch POIs for the most recent common blocks - let pois = indexers + indexers .iter() .map(|indexer| async { let poi_requests = latest_blocks @@ -143,7 +190,7 @@ pub async fn query_proofs_of_indexing( .filter_map(|(deployment, block_number)| { block_number.map(|block_number| PoiRequest { deployment: deployment.clone(), - block_number: block_number, + block_number, }) }) .collect::>(); @@ -151,7 +198,7 @@ pub async fn query_proofs_of_indexing( let pois = indexer.clone().proofs_of_indexing(poi_requests).await; debug!( - id = %indexer.id(), pois = %pois.len(), + id = %indexer.address_string(), pois = %pois.len(), "Successfully queried POIs from indexer" ); @@ -162,7 +209,5 @@ pub async fn query_proofs_of_indexing( .await .into_iter() .flatten() - .collect::>(); - - pois + .collect::>() } diff --git a/backend/crates/common/src/test_utils/gen.rs b/crates/graphix-lib/src/test_utils/gen.rs similarity index 93% rename from backend/crates/common/src/test_utils/gen.rs rename to crates/graphix-lib/src/test_utils/gen.rs index 868396d..0a04a47 100644 --- a/backend/crates/common/src/test_utils/gen.rs +++ b/crates/graphix-lib/src/test_utils/gen.rs @@ -1,12 +1,12 @@ use std::iter::repeat_with; use std::sync::Arc; +use graphix_indexer_client::{BlockPointer, Bytes32, Indexer, SubgraphDeployment}; use rand::distributions::Alphanumeric; use rand::seq::IteratorRandom; use rand::Rng; use super::mocks::{DeploymentDetails, MockIndexer, PartialProofOfIndexing}; -use crate::prelude::{BlockPointer, Bytes32, Indexer, SubgraphDeployment}; pub fn gen_deployments() -> Vec { vec![ @@ -24,7 +24,7 @@ pub fn gen_blocks() -> Vec { let block_hash = |n: u64| -> Bytes32 { let mut buf = [0u8; 32]; buf[24..32].clone_from_slice(&n.to_be_bytes()); - Bytes32(buf.into()) + Bytes32(buf) }; (0..10) .map(|number| BlockPointer { @@ -88,14 +88,14 @@ where .map(|deployment| DeploymentDetails { deployment, network: "mainnet".into(), - latest_block: blocks.iter().choose(&mut rng).unwrap().clone(), + latest_block: *blocks.iter().choose(&mut rng).unwrap(), canonical_pois: gen_pois(blocks.clone(), &mut rng), earliest_block_num: blocks[0].number, }) .collect(); Arc::new(MockIndexer { - id, + name: id, deployment_details, fail_indexing_statuses: rng.gen_bool(0.1), }) as Arc diff --git a/backend/crates/common/src/test_utils/mocks.rs b/crates/graphix-lib/src/test_utils/mocks.rs similarity index 82% rename from backend/crates/common/src/test_utils/mocks.rs rename to crates/graphix-lib/src/test_utils/mocks.rs index 4e4dbe0..c621e25 100644 --- a/backend/crates/common/src/test_utils/mocks.rs +++ b/crates/graphix-lib/src/test_utils/mocks.rs @@ -1,9 +1,10 @@ +use std::borrow::Cow; use std::sync::Arc; use anyhow::anyhow; use async_trait::async_trait; - -use crate::prelude::{ +use graphix_common_types::IndexerVersion; +use graphix_indexer_client::{ BlockPointer, Bytes32, CachedEthereumCall, EntityChanges, Indexer, IndexingStatus, PoiRequest, ProofOfIndexing, SubgraphDeployment, }; @@ -19,19 +20,19 @@ pub struct DeploymentDetails { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MockIndexer { - pub id: String, + pub name: String, pub deployment_details: Vec, pub fail_indexing_statuses: bool, } #[async_trait] impl Indexer for MockIndexer { - fn id(&self) -> &str { - &self.id + fn name(&self) -> Option> { + Some(Cow::Borrowed(&self.name)) } - fn address(&self) -> Option<&[u8]> { - None + fn address(&self) -> &[u8] { + self.name.as_bytes() } async fn indexing_statuses(self: Arc) -> Result, anyhow::Error> { @@ -80,12 +81,26 @@ impl Indexer for MockIndexer { .map(|(deployment_detail, poi)| ProofOfIndexing { indexer: self.clone(), deployment: deployment_detail.deployment.clone(), - block: poi.block.clone(), - proof_of_indexing: poi.proof_of_indexing.clone(), + block: poi.block, + proof_of_indexing: poi.proof_of_indexing, }) .collect::>() } + async fn version(self: Arc) -> anyhow::Result { + Ok(IndexerVersion { + version: "0.0.0".to_string(), + commit: "no-commit-hash".to_string(), + }) + } + + async fn subgraph_api_versions( + self: Arc, + _subgraph_id: &str, + ) -> anyhow::Result> { + Ok(vec![]) + } + async fn cached_eth_calls( self: Arc, _network: &str, diff --git a/crates/graphix-lib/src/test_utils/mod.rs b/crates/graphix-lib/src/test_utils/mod.rs new file mode 100644 index 0000000..cbcb292 --- /dev/null +++ b/crates/graphix-lib/src/test_utils/mod.rs @@ -0,0 +1,53 @@ +pub mod gen; +pub mod mocks; + +use std::env; +use std::sync::Arc; + +use graphix_indexer_client::{Indexer, RealIndexer, SubgraphDeployment}; +use once_cell::sync::Lazy; +use prometheus_exporter::prometheus::IntCounterVec; +use rand::rngs::{OsRng, SmallRng}; +use rand::{RngCore, SeedableRng}; +use url::Url; + +use crate::config::IndexerConfig; + +pub static TEST_SEED: Lazy = Lazy::new(|| { + let seed = env::var("TEST_SEED") + .map(|seed| seed.parse().expect("Invalid TEST_SEED value")) + .unwrap_or(OsRng.next_u64()); + + println!("------------------------------------------------------------------------"); + println!("TEST_SEED={}", seed); + println!(" This value can be changed via the environment variable TEST_SEED."); + println!("------------------------------------------------------------------------"); + + seed +}); + +/// Test utility function to create a valid `Indexer` from an arbitrary base url. +pub fn test_indexer_from_url(url: impl Into) -> Arc { + let url: Url = url.into().parse().expect("Invalid status url"); + let conf = IndexerConfig { + name: Some(url.host().unwrap().to_string()), + address: url.as_str().as_bytes().to_owned(), + index_node_endpoint: url.join("status").unwrap(), + }; + Arc::new(RealIndexer::new( + conf.name, + conf.address, + conf.index_node_endpoint.to_string(), + IntCounterVec::new(prometheus::Opts::new("foo", "bar"), &["a", "b"]).unwrap(), + )) +} + +/// Test utility function to create a valid `SubgraphDeployment` with an arbitrary deployment +/// id/ipfs hash. +pub fn test_deployment_id(deployment: impl Into) -> SubgraphDeployment { + SubgraphDeployment(deployment.into()) +} + +pub fn fast_rng(seed_extra: u64) -> SmallRng { + SmallRng::seed_from_u64(*TEST_SEED + seed_extra) +} diff --git a/backend/crates/common/tests/indexing_statuses.rs b/crates/graphix-lib/tests/indexing_statuses.rs similarity index 83% rename from backend/crates/common/tests/indexing_statuses.rs rename to crates/graphix-lib/tests/indexing_statuses.rs index 5a24e6d..a58869a 100644 --- a/backend/crates/common/tests/indexing_statuses.rs +++ b/crates/graphix-lib/tests/indexing_statuses.rs @@ -1,10 +1,10 @@ use futures::stream::FuturesUnordered; use futures::{future, StreamExt}; -use graphix_common::metrics; -use graphix_common::prelude::IndexingStatus; -use graphix_common::queries::query_indexing_statuses; -use graphix_common::test_utils::fast_rng; -use graphix_common::test_utils::gen::*; +use graphix_indexer_client::IndexingStatus; +use graphix_lib::metrics; +use graphix_lib::queries::query_indexing_statuses; +use graphix_lib::test_utils::fast_rng; +use graphix_lib::test_utils::gen::*; #[tokio::test] async fn indexing_statuses() { diff --git a/crates/graphix-lib/tests/it_indexing_statuses.rs b/crates/graphix-lib/tests/it_indexing_statuses.rs new file mode 100644 index 0000000..4b4ffb6 --- /dev/null +++ b/crates/graphix-lib/tests/it_indexing_statuses.rs @@ -0,0 +1,27 @@ +use std::time::Duration; + +use graphix_indexer_client::Indexer; +use graphix_lib::test_utils::{test_deployment_id, test_indexer_from_url}; + +#[tokio::test] +async fn send_indexer_statuses_query() { + //// Given + let indexer = test_indexer_from_url("https://testnet-indexer-03-europe-cent.thegraph.com"); + + let test_deployment = test_deployment_id("QmeYTH2fK2wv96XvnCGH2eyKFE8kmRfo53zYVy5dKysZtH"); + + //// When + let request_fut = Indexer::indexing_statuses(indexer); + let response = tokio::time::timeout(Duration::from_secs(10), request_fut) + .await + .expect("Timeout"); + + //// Then + assert!(response.is_ok()); + + let response = response.unwrap(); + assert!(!response.is_empty()); + assert!(response + .iter() + .any(|status| status.deployment == test_deployment)); +} diff --git a/backend/crates/common/tests/it_proofs_of_indexing.rs b/crates/graphix-lib/tests/it_proofs_of_indexing.rs similarity index 84% rename from backend/crates/common/tests/it_proofs_of_indexing.rs rename to crates/graphix-lib/tests/it_proofs_of_indexing.rs index bd0db73..c790d9b 100644 --- a/backend/crates/common/tests/it_proofs_of_indexing.rs +++ b/crates/graphix-lib/tests/it_proofs_of_indexing.rs @@ -1,30 +1,7 @@ -use std::sync::Arc; use std::time::Duration; -use reqwest::Url; - -use graphix_common::config::IndexerUrls; -use graphix_common::prelude::{ - Indexer, IndexerConfig, PoiRequest, RealIndexer, SubgraphDeployment, -}; - -/// Test utility function to create a valid `Indexer` from an arbitrary base url. -fn test_indexer_from_url(url: impl Into) -> Arc { - let url: Url = url.into().parse().expect("Invalid status url"); - let conf = IndexerConfig { - name: url.host().unwrap().to_string(), - urls: IndexerUrls { - status: url.join("status").unwrap(), - }, - }; - Arc::new(RealIndexer::new(conf)) -} - -/// Test utility function to create a valid `SubgraphDeployment` with an arbitrary deployment -/// id/ipfs hash. -fn test_deployment_id(deployment: impl Into) -> SubgraphDeployment { - SubgraphDeployment(deployment.into()) -} +use graphix_indexer_client::{Indexer, PoiRequest}; +use graphix_lib::test_utils::{test_deployment_id, test_indexer_from_url}; #[tokio::test] async fn send_single_query_and_process_result() { @@ -108,7 +85,7 @@ async fn send_single_query_of_unknown_block_number_and_handle_error() { #[tokio::test] async fn send_multiple_queries_and_process_results() { - //// Given + // Given // FIXME: This is temporarily set to 1 until we fix the error: 'Null value resolved for // non-null field `proofOfIndexing`' Which is probably a Graph Node bug. Setting it to 1 @@ -126,13 +103,13 @@ async fn send_multiple_queries_and_process_results() { }) .collect::>(); - //// When + // When let request_fut = Indexer::proofs_of_indexing(indexer, poi_requests); let response = tokio::time::timeout(Duration::from_secs(10), request_fut) .await .expect("Timeout"); - //// Then + // Then assert_eq!(response.len(), MAX_REQUESTS_PER_QUERY + 2); assert_eq!(response[0].deployment, deployment); diff --git a/backend/crates/common/tests/proofs_of_indexing.rs b/crates/graphix-lib/tests/proofs_of_indexing.rs similarity index 84% rename from backend/crates/common/tests/proofs_of_indexing.rs rename to crates/graphix-lib/tests/proofs_of_indexing.rs index 18215ab..c85086c 100644 --- a/backend/crates/common/tests/proofs_of_indexing.rs +++ b/crates/graphix-lib/tests/proofs_of_indexing.rs @@ -1,9 +1,9 @@ use std::collections::BTreeSet; -use graphix_common::block_choice::BlockChoicePolicy; -use graphix_common::test_utils::fast_rng; -use graphix_common::test_utils::gen::gen_indexers; -use graphix_common::{metrics, queries}; +use graphix_lib::block_choice::BlockChoicePolicy; +use graphix_lib::test_utils::fast_rng; +use graphix_lib::test_utils::gen::gen_indexers; +use graphix_lib::{metrics, queries}; use itertools::Itertools; #[tokio::test] diff --git a/backend/crates/cross-checker/Cargo.toml b/crates/graphix/Cargo.toml similarity index 54% rename from backend/crates/cross-checker/Cargo.toml rename to crates/graphix/Cargo.toml index e4d720f..292a4ce 100644 --- a/backend/crates/cross-checker/Cargo.toml +++ b/crates/graphix/Cargo.toml @@ -1,21 +1,20 @@ [package] -name = "graphix-cross-checker" +name = "graphix" version = "0.1.0" authors = ["Jannis Pohlmann "] edition = "2021" -default-run = "graphix-cross-checker" - -[[bin]] -name = "graphix-cross-checker" -path = "src/main.rs" [dependencies] -anyhow = "1.0.48" +anyhow = "1" +async-graphql = "7" async-trait = "0.1.52" -clap = { version = "4", features = ["derive"] } -eventuals = "0.6.6" +clap = { version = "4", features = ["derive", "env"] } futures = "0.3.18" -graphix-common = { path = "../common" } +graphix_common_types = { path = "../common-types" } +graphix_indexer_client = { path = "../indexer-client" } +graphix_lib = { path = "../graphix-lib" } +graphix_network_sg_client = { path = "../network-sg-client" } +graphix_store = { path = "../store" } nanoid = "0.4.0" prometheus_exporter = "0.8.5" serde_json = "1" @@ -24,10 +23,13 @@ tokio = { version = "1.14.0", features = ["full"] } tracing = "0.1.29" tracing-subscriber = { version = "0.3.2", features = ["env-filter"] } uuid = { version = "0.8.2", features = ["v4"] } -warp = "0.3.2" + +# From api-server +async-graphql-axum = "7" +axum = "0.7" [dev-dependencies] -graphix-common = { path = "../common", features = ["tests"] } +graphix_lib = { path = "../graphix-lib", features = ["tests"] } hex = "0.4.3" once_cell = "1.9.0" rand = { version = "0.8.4", features = ["small_rng"] } diff --git a/crates/graphix/main.rs b/crates/graphix/main.rs new file mode 100644 index 0000000..44706ed --- /dev/null +++ b/crates/graphix/main.rs @@ -0,0 +1,61 @@ +use std::net::Ipv4Addr; + +use async_graphql::http::GraphiQLSource; +use async_graphql_axum::GraphQL; +use axum::response::IntoResponse; +use axum::Router; +use clap::Parser; +use graphix_lib::graphql_api::{self}; +use graphix_lib::store::Store; +use tokio::net::TcpListener; + +#[derive(Parser, Debug)] +pub struct CliOptions { + #[clap(long, default_value = "80", env = "GRAPHIX_PORT")] + pub port: u16, + + #[clap( + long, + default_value = "postgresql://localhost:5432/graphix", + env = "GRAPHIX_DATABASE_URL" + )] + pub database_url: String, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + init_tracing(); + + let cli_options = CliOptions::parse(); + + // Listen to requests forever. + axum::serve( + TcpListener::bind((Ipv4Addr::UNSPECIFIED, cli_options.port)).await?, + axum_server(cli_options).await?, + ) + .await?; + + Ok(()) +} + +async fn axum_server(cli_options: CliOptions) -> anyhow::Result> { + use axum::routing::get; + + let store = Store::new(cli_options.database_url.as_str()).await?; + let api_schema = graphql_api::api_schema(graphql_api::ApiSchemaContext { store }); + + Ok(axum::Router::new() + .route("/", get(|| async { "Ready to roll!" })) + .route( + "/graphql", + get(graphiql_route).post_service(GraphQL::new(api_schema)), + )) +} + +fn init_tracing() { + tracing_subscriber::fmt::init(); +} + +async fn graphiql_route() -> impl IntoResponse { + axum::response::Html(GraphiQLSource::build().endpoint("/graphql").finish()) +} diff --git a/backend/crates/cross-checker/src/bisect.rs b/crates/graphix/src/bisect.rs similarity index 94% rename from backend/crates/cross-checker/src/bisect.rs rename to crates/graphix/src/bisect.rs index c72ead0..3150540 100644 --- a/backend/crates/cross-checker/src/bisect.rs +++ b/crates/graphix/src/bisect.rs @@ -1,15 +1,15 @@ use std::sync::Arc; use std::time::Duration; -use graphix_common::graphql_api::types::{ +use graphix_common_types::{ BisectionReport, BisectionRunReport, DivergenceBlockBounds, DivergenceInvestigationReport, - DivergenceInvestigationRequest, DivergenceInvestigationStatus, PartialBlock, + DivergenceInvestigationRequest, DivergenceInvestigationStatus, + DivergingBlock as DivergentBlock, PartialBlock, }; -use graphix_common::prelude::{ - BlockPointer, DivergingBlock as DivergentBlock, Indexer, PoiRequest, ProofOfIndexing, - SubgraphDeployment, +use graphix_indexer_client::{ + BlockPointer, Indexer, IndexerId, PoiRequest, ProofOfIndexing, SubgraphDeployment, }; -use graphix_common::store::Store; +use graphix_store::Store; use thiserror::Error; use tokio::sync::watch; use tracing::{debug, error, info}; @@ -25,9 +25,12 @@ pub struct DivergingBlock { impl From for DivergentBlock { fn from(other: DivergingBlock) -> DivergentBlock { Self { - block: other.poi1.block, - proof_of_indexing1: other.poi1.proof_of_indexing, - proof_of_indexing2: other.poi2.proof_of_indexing, + block: PartialBlock { + number: other.poi1.block.number as _, + hash: other.poi1.block.hash.map(|h| h.to_string()), + }, + proof_of_indexing1: other.poi1.proof_of_indexing.to_string(), + proof_of_indexing2: other.poi2.proof_of_indexing.to_string(), } } } @@ -240,7 +243,7 @@ async fn handle_divergence_investigation_request_pair( debug!(req_uuid = req_uuid_str, poi1 = %poi1_s, poi2 = %poi2_s, "Fetching Pois"); let poi1 = match store - .poi(&poi1_s) + .poi(poi1_s) .map_err(DivergenceInvestigationError::Database) .and_then(|poi_opt| { if let Some(poi) = poi_opt { @@ -258,7 +261,7 @@ async fn handle_divergence_investigation_request_pair( } }; let poi2 = match store - .poi(&poi2_s) + .poi(poi2_s) .map_err(DivergenceInvestigationError::Database) .and_then(|poi_opt| { if let Some(poi) = poi_opt { @@ -311,7 +314,7 @@ async fn handle_divergence_investigation_request_pair( debug!(req_uuid = req_uuid_str, poi1 = %poi1_s, poi2 = %poi2_s, "Fetching indexers"); let indexer1 = match indexers .iter() - .find(|indexer| Some(indexer.id()) == poi1.indexer.name.as_deref()) + .find(|indexer| indexer.address() == poi1.indexer.address()) .cloned() .ok_or(DivergenceInvestigationError::IndexerNotFound { poi: poi1_s.to_string(), @@ -324,7 +327,7 @@ async fn handle_divergence_investigation_request_pair( }; let indexer2 = match indexers .iter() - .find(|indexer| Some(indexer.id()) == poi2.indexer.name.as_deref()) + .find(|indexer| indexer.address() == poi2.indexer.address()) .cloned() .ok_or(DivergenceInvestigationError::IndexerNotFound { poi: poi2_s.to_string(), @@ -337,10 +340,10 @@ async fn handle_divergence_investigation_request_pair( }; debug!(req_uuid = req_uuid_str, poi1 = %poi1_s, poi2 = %poi2_s, "Fetched indexers"); - if indexer1.id() == indexer2.id() { + if indexer1.address() == indexer2.address() { report.error = Some( DivergenceInvestigationError::SameIndexer { - indexer_id: indexer1.id().to_string(), + indexer_id: indexer1.address_string(), } .to_string(), ); diff --git a/crates/graphix/src/main.rs b/crates/graphix/src/main.rs new file mode 100644 index 0000000..24fc4cf --- /dev/null +++ b/crates/graphix/src/main.rs @@ -0,0 +1,160 @@ +#![allow(clippy::type_complexity)] + +mod bisect; +mod utils; + +use std::collections::HashSet; +use std::net::Ipv4Addr; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::Duration; + +use async_graphql::http::GraphiQLSource; +use async_graphql_axum::GraphQL; +use axum::response::IntoResponse; +use axum::Router; +use clap::Parser; +use graphix_indexer_client::{Indexer, IndexerId}; +use graphix_lib::config::Config; +use graphix_lib::graphql_api::{self}; +use graphix_lib::queries::{query_indexing_statuses, query_proofs_of_indexing}; +use graphix_lib::{config, metrics, PrometheusExporter}; +use graphix_store::{PoiLiveness, Store}; +use prometheus_exporter::prometheus; +use tokio::net::TcpListener; +use tokio::sync::watch; +use tracing::*; + +use crate::bisect::handle_divergence_investigation_requests; + +#[derive(Parser, Debug)] +struct CliOptions { + #[clap(long)] + config: PathBuf, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + init_tracing(); + + info!("Parse options"); + let cli_options = CliOptions::parse(); + + info!("Loading configuration file"); + let config = Config::read(&cli_options.config)?; + + info!("Initialize store and running migrations"); + let store = Store::new(&config.database_url).await?; + info!("Store initialization successful"); + + if config.enable_api { + let config = config.clone(); + tokio::spawn(async move { + // Listen to requests forever. + axum::serve( + TcpListener::bind((Ipv4Addr::UNSPECIFIED, config.api_port)).await?, + axum_server(&config).await?, + ) + .await?; + + Result::<(), anyhow::Error>::Ok(()) + }); + } + + if config.enable_indexing { + let sleep_duration = Duration::from_secs(config.polling_period_in_seconds); + + // Prometheus metrics. + let registry = prometheus::default_registry().clone(); + let _exporter = + PrometheusExporter::start(config.prometheus_port, registry.clone()).unwrap(); + + info!("Initializing bisect request handler"); + let store_clone = store.clone(); + let (tx_indexers, rx_indexers) = watch::channel(vec![]); + tokio::spawn(async move { + handle_divergence_investigation_requests(&store_clone, rx_indexers) + .await + .unwrap() + }); + + loop { + info!("New main loop iteration"); + info!("Initialize inputs (indexers, indexing statuses etc.)"); + + let mut indexers = config::config_to_indexers(config.clone(), metrics()).await?; + // Different data sources, especially network subgraphs, result in + // duplicate indexers. + indexers = deduplicate_indexers(&indexers); + + store.write_indexers(&indexers)?; + + tx_indexers.send(indexers.clone())?; + + let graph_node_versions = + graphix_lib::queries::query_graph_node_versions(&indexers, metrics()).await; + store.write_graph_node_versions(graph_node_versions)?; + + let indexing_statuses = query_indexing_statuses(indexers, metrics()).await; + + info!("Monitor proofs of indexing"); + let pois = + query_proofs_of_indexing(indexing_statuses, config.block_choice_policy).await; + + info!(pois = pois.len(), "Finished tracking Pois"); + + let write_err = store.write_pois(&pois, PoiLiveness::Live).err(); + if let Some(err) = write_err { + error!(error = %err, "Failed to write POIs to database"); + } + + info!( + sleep_seconds = sleep_duration.as_secs(), + "Sleeping for a while before next main loop iteration" + ); + tokio::time::sleep(sleep_duration).await; + } + } + + Ok(()) +} + +fn init_tracing() { + tracing_subscriber::fmt::init(); +} + +fn deduplicate_indexers(indexers: &[Arc]) -> Vec> { + info!(len = indexers.len(), "Deduplicating indexers"); + let mut seen = HashSet::new(); + let mut deduplicated = vec![]; + for indexer in indexers { + if !seen.contains(indexer.address()) { + deduplicated.push(indexer.clone()); + seen.insert(indexer.address()); + } + } + info!( + len = deduplicated.len(), + delta = indexers.len() - deduplicated.len(), + "Successfully deduplicated indexers" + ); + deduplicated +} + +async fn axum_server(config: &Config) -> anyhow::Result> { + use axum::routing::get; + + let store = Store::new(config.database_url.as_str()).await?; + let api_schema = graphql_api::api_schema(graphql_api::ApiSchemaContext { store }); + + Ok(axum::Router::new() + .route("/", get(|| async { "Ready to roll!" })) + .route( + "/graphql", + get(graphiql_route).post_service(GraphQL::new(api_schema)), + )) +} + +async fn graphiql_route() -> impl IntoResponse { + axum::response::Html(GraphiQLSource::build().endpoint("/graphql").finish()) +} diff --git a/backend/crates/cross-checker/src/utils.rs b/crates/graphix/src/utils.rs similarity index 93% rename from backend/crates/cross-checker/src/utils.rs rename to crates/graphix/src/utils.rs index d7ad8b7..b3b8c1c 100644 --- a/backend/crates/cross-checker/src/utils.rs +++ b/crates/graphix/src/utils.rs @@ -4,8 +4,8 @@ use std::collections::HashSet; use std::hash::Hash; use std::sync::Arc; -use graphix_common::prelude::Indexer; -use graphix_common::store; +use graphix_indexer_client::Indexer; +use graphix_store::Store; use crate::bisect::DivergenceInvestigationError; @@ -26,11 +26,11 @@ where /// Given a Poi, find any of the indexers that have been known to produce it. pub fn find_any_indexer_for_poi( - store: &store::Store, + store: &Store, poi_s: &str, indexers: &[Arc], ) -> anyhow::Result>> { - let poi = if let Some(poi) = store.poi(&poi_s)? { + let poi = if let Some(poi) = store.poi(poi_s)? { poi } else { return Ok(None); @@ -38,14 +38,14 @@ pub fn find_any_indexer_for_poi( let indexer_opt = indexers .iter() - .find(|indexer| indexer.address() == poi.indexer.address.as_deref()) + .find(|indexer| indexer.address() == poi.indexer.address) .cloned(); Ok(indexer_opt) } pub fn find_indexer_pair( - store: &store::Store, + store: &Store, poi_1: &str, poi_2: &str, indexers: &[Arc], diff --git a/crates/indexer-client/Cargo.toml b/crates/indexer-client/Cargo.toml new file mode 100644 index 0000000..030b6ff --- /dev/null +++ b/crates/indexer-client/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "graphix_indexer_client" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +async-trait = "0.1" +async-graphql = "7" +graphix_common_types = { path = "../common-types" } +graphql_client = "0.13" +hex = "0.4" +prometheus = { version = "0.13", default-features = false } +reqwest = { version = "0.11", features = ["json"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tracing = "0.1" + +[build-dependencies] +reqwest = { version = "0.11", features = ["blocking"] } diff --git a/backend/crates/common/build.rs b/crates/indexer-client/build.rs similarity index 100% rename from backend/crates/common/build.rs rename to crates/indexer-client/build.rs diff --git a/backend/crates/common/graphql/indexer/queries/block-data.gql b/crates/indexer-client/graphql/indexer/queries/block-data.gql similarity index 100% rename from backend/crates/common/graphql/indexer/queries/block-data.gql rename to crates/indexer-client/graphql/indexer/queries/block-data.gql diff --git a/backend/crates/common/graphql/indexer/queries/cached-eth-calls.gql b/crates/indexer-client/graphql/indexer/queries/cached-eth-calls.gql similarity index 100% rename from backend/crates/common/graphql/indexer/queries/cached-eth-calls.gql rename to crates/indexer-client/graphql/indexer/queries/cached-eth-calls.gql diff --git a/backend/crates/common/graphql/indexer/queries/entity-changes-in-block.gql b/crates/indexer-client/graphql/indexer/queries/entity-changes-in-block.gql similarity index 100% rename from backend/crates/common/graphql/indexer/queries/entity-changes-in-block.gql rename to crates/indexer-client/graphql/indexer/queries/entity-changes-in-block.gql diff --git a/crates/indexer-client/graphql/indexer/queries/indexer-version.gql b/crates/indexer-client/graphql/indexer/queries/indexer-version.gql new file mode 100644 index 0000000..4020655 --- /dev/null +++ b/crates/indexer-client/graphql/indexer/queries/indexer-version.gql @@ -0,0 +1,6 @@ +query IndexerVersion { + version { + version + commit + } +} diff --git a/backend/crates/common/graphql/indexer/queries/indexing-statuses.gql b/crates/indexer-client/graphql/indexer/queries/indexing-statuses.gql similarity index 100% rename from backend/crates/common/graphql/indexer/queries/indexing-statuses.gql rename to crates/indexer-client/graphql/indexer/queries/indexing-statuses.gql diff --git a/backend/crates/common/graphql/indexer/queries/pois.gql b/crates/indexer-client/graphql/indexer/queries/pois.gql similarity index 100% rename from backend/crates/common/graphql/indexer/queries/pois.gql rename to crates/indexer-client/graphql/indexer/queries/pois.gql diff --git a/crates/indexer-client/graphql/indexer/queries/subgraph-api-versions.gql b/crates/indexer-client/graphql/indexer/queries/subgraph-api-versions.gql new file mode 100644 index 0000000..fb14073 --- /dev/null +++ b/crates/indexer-client/graphql/indexer/queries/subgraph-api-versions.gql @@ -0,0 +1,5 @@ +query SubgraphApiVersions($subgraphId: String!) { + apiVersions(subgraphId: $subgraphId) { + version + } +} diff --git a/backend/crates/common/graphql/indexer/queries/typename.gql b/crates/indexer-client/graphql/indexer/queries/typename.gql similarity index 100% rename from backend/crates/common/graphql/indexer/queries/typename.gql rename to crates/indexer-client/graphql/indexer/queries/typename.gql diff --git a/backend/crates/common/graphql/indexer/schema.gql b/crates/indexer-client/graphql/indexer/schema.gql similarity index 100% rename from backend/crates/common/graphql/indexer/schema.gql rename to crates/indexer-client/graphql/indexer/schema.gql diff --git a/backend/crates/common/graphql/touch-this-file-to-refetch-schema b/crates/indexer-client/graphql/touch-this-file-to-refetch-schema similarity index 100% rename from backend/crates/common/graphql/touch-this-file-to-refetch-schema rename to crates/indexer-client/graphql/touch-this-file-to-refetch-schema diff --git a/backend/crates/common/src/indexer/interceptor.rs b/crates/indexer-client/src/interceptor.rs similarity index 78% rename from backend/crates/common/src/indexer/interceptor.rs rename to crates/indexer-client/src/interceptor.rs index c76e101..984317d 100644 --- a/backend/crates/common/src/indexer/interceptor.rs +++ b/crates/indexer-client/src/interceptor.rs @@ -1,13 +1,13 @@ //! A indexer interceptor, for test configs only. +use std::borrow::Cow; use std::sync::Arc; use async_trait::async_trait; +use graphix_common_types::IndexerVersion; use super::{CachedEthereumCall, EntityChanges}; -use crate::indexer::Indexer; -use crate::prelude::Bytes32; -use crate::types::{IndexingStatus, PoiRequest, ProofOfIndexing}; +use crate::{Bytes32, Indexer, IndexingStatus, PoiRequest, ProofOfIndexing}; /// Pretends to be an indexer by routing requests a /// [`RealIndexer`](crate::indexer::RealIndexer) and then intercepting the @@ -16,29 +16,26 @@ use crate::types::{IndexingStatus, PoiRequest, ProofOfIndexing}; #[derive(Debug)] pub struct IndexerInterceptor { target: Arc, - id: String, poi_byte: u8, } impl IndexerInterceptor { - pub fn new(id: String, target: Arc, poi_byte: u8) -> Self { - Self { - id, - target, - poi_byte, - } + pub fn new(target: Arc, poi_byte: u8) -> Self { + Self { target, poi_byte } } } #[async_trait] impl Indexer for IndexerInterceptor { - fn id(&self) -> &str { - &self.id + fn name(&self) -> Option> { + self.target + .name() + .map(|name| Cow::Owned(format!("interceptor-{}", name))) } - fn address(&self) -> Option<&[u8]> { - None + fn address(&self) -> &[u8] { + self.target.address() } async fn ping(self: Arc) -> anyhow::Result<()> { @@ -60,6 +57,10 @@ impl Indexer for IndexerInterceptor { Ok(hijacked_statuses) } + async fn version(self: Arc) -> anyhow::Result { + self.target.clone().version().await + } + async fn proofs_of_indexing( self: Arc, requests: Vec, @@ -79,6 +80,13 @@ impl Indexer for IndexerInterceptor { .collect() } + async fn subgraph_api_versions( + self: Arc, + subgraph_id: &str, + ) -> anyhow::Result> { + self.target.clone().subgraph_api_versions(subgraph_id).await + } + async fn cached_eth_calls( self: Arc, network: &str, diff --git a/crates/indexer-client/src/lib.rs b/crates/indexer-client/src/lib.rs new file mode 100644 index 0000000..0b4a613 --- /dev/null +++ b/crates/indexer-client/src/lib.rs @@ -0,0 +1,352 @@ +mod interceptor; +mod real_indexer; + +use std::borrow::Cow; +use std::collections::HashMap; +use std::fmt::{self, Debug, Display}; +use std::hash::Hash; +use std::ops::Deref; +use std::sync::Arc; + +use anyhow::anyhow; +use async_trait::async_trait; +use graphix_common_types::IndexerVersion; +pub use interceptor::IndexerInterceptor; +pub use real_indexer::RealIndexer; +use serde::{Deserialize, Serialize}; + +/// An indexer is a `graph-node` instance that can be queried for information. +#[async_trait] +pub trait Indexer: Send + Sync + Debug { + /// The indexer's address. + fn address(&self) -> &[u8]; + + /// Human-readable name of the indexer. + fn name(&self) -> Option>; + + async fn ping(self: Arc) -> anyhow::Result<()>; + + async fn indexing_statuses(self: Arc) -> anyhow::Result>; + + async fn proofs_of_indexing(self: Arc, requests: Vec) + -> Vec; + + async fn version(self: Arc) -> anyhow::Result; + + async fn subgraph_api_versions( + self: Arc, + subgraph_id: &str, + ) -> anyhow::Result>; + + /// Convenience wrapper around calling [`Indexer::proofs_of_indexing`] for a + /// single POI. + async fn proof_of_indexing( + self: Arc, + request: PoiRequest, + ) -> Result { + let pois = self.proofs_of_indexing(vec![request.clone()]).await; + match pois.len() { + 0 => return Err(anyhow!("no proof of indexing returned {:?}", request)), + 1 => return Ok(pois.into_iter().next().unwrap()), + _ => return Err(anyhow!("multiple proofs of indexing returned")), + } + } + + /// Returns the cached Ethereum calls for the given block hash. + async fn cached_eth_calls( + self: Arc, + network: &str, + block_hash: &[u8], + ) -> anyhow::Result>; + + /// Returns the block cache contents for the given block hash. + async fn block_cache_contents( + self: Arc, + network: &str, + block_hash: &[u8], + ) -> anyhow::Result>; + + /// Returns the entity changes for the given block number. + async fn entity_changes( + self: Arc, + subgraph_id: &str, + block_number: u64, + ) -> anyhow::Result; +} + +/// Graphix defines an indexer's ID as either its Ethereum address (if it has +/// one) or its name (if it doesn't have an address i.e. it's not a network +/// participant), strictly in this order. +pub trait IndexerId { + fn address(&self) -> &[u8]; + fn name(&self) -> Option>; + + /// Returns the string representation of the indexer's address using + /// [`HexString`]. + fn address_string(&self) -> String { + HexString(self.address()).to_string() + } +} + +impl IndexerId for T +where + T: Indexer, +{ + fn address(&self) -> &[u8] { + Indexer::address(self) + } + + fn name(&self) -> Option> { + Indexer::name(self) + } +} + +impl IndexerId for Arc { + fn address(&self) -> &[u8] { + Indexer::address(&**self) + } + + fn name(&self) -> Option> { + Indexer::name(&**self) + } +} + +impl PartialEq for dyn Indexer { + fn eq(&self, other: &Self) -> bool { + self.address() == other.address() + } +} + +impl Eq for dyn Indexer {} + +impl Hash for dyn Indexer { + fn hash(&self, state: &mut H) { + // It's best to hash addresses even though entropy is typically already + // high, because some Graphix configurations may use human-readable + // strings as fake addresses. + self.address().hash(state) + } +} + +impl PartialOrd for dyn Indexer { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for dyn Indexer { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.address().cmp(other.address()) + } +} + +/// A wrapper around some inner data `T` that has an associated [`Indexer`]. +pub struct WithIndexer { + pub indexer: Arc, + pub inner: T, +} + +impl WithIndexer { + pub fn new(indexer: Arc, inner: T) -> Self { + Self { indexer, inner } + } +} + +#[derive(Debug)] +pub struct CachedEthereumCall { + pub id_hash: Vec, + pub return_value: Vec, + pub contract_address: Vec, +} + +pub type EntityType = String; +pub type EntityId = String; + +pub struct EntityChanges { + pub updates: HashMap>, + pub deletions: HashMap>, +} + +#[derive(Copy, Clone, Debug)] +pub struct HexString(pub T); + +impl> Display for HexString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0.as_ref())) + } +} + +impl> Serialize for HexString { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_str(&self) + } +} + +impl<'a> Deserialize<'a> for HexString> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'a>, + { + let s = String::deserialize(deserializer)?; + if !s.starts_with("0x") { + return Err(serde::de::Error::custom("hexstring must start with 0x")); + } + hex::decode(&s[2..]) + .map(Self) + .map_err(serde::de::Error::custom) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Ord, PartialOrd)] +pub struct BlockPointer { + pub number: u64, + pub hash: Option, +} + +impl fmt::Display for BlockPointer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "#{} ({})", + self.number, + self.hash + .as_ref() + .map_or("no hash".to_string(), |hash| format!("{}", hash)) + ) + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub struct SubgraphDeployment(pub String); + +impl Deref for SubgraphDeployment { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Clone, Eq)] +pub struct IndexingStatus { + pub indexer: Arc, + pub deployment: SubgraphDeployment, + pub network: String, + pub latest_block: BlockPointer, + pub earliest_block_num: u64, +} + +impl PartialEq for IndexingStatus { + fn eq(&self, other: &Self) -> bool { + self.indexer.as_ref() == other.indexer.as_ref() + && self.deployment == other.deployment + && self.network == other.network + && self.latest_block == other.latest_block + } +} + +/// A 32-byte array that can be easily converted to and from hex strings. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Ord, PartialOrd)] +pub struct Bytes32(pub [u8; 32]); + +impl TryFrom<&str> for Bytes32 { + type Error = anyhow::Error; + + fn try_from(s: &str) -> Result { + Ok(Self(hex::FromHex::from_hex(s.trim_start_matches("0x"))?)) + } +} + +impl TryFrom> for Bytes32 { + type Error = anyhow::Error; + + fn try_from(v: Vec) -> Result { + if v.len() != 32 { + return Err(anyhow::anyhow!("Expected 32 bytes, got {}", v.len())); + } + let mut bytes = [0u8; 32]; + bytes.copy_from_slice(&v); + Ok(Self(bytes)) + } +} + +impl fmt::Display for Bytes32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + +#[derive(Debug, Clone, Eq, PartialOrd, Ord)] +pub struct ProofOfIndexing { + pub indexer: Arc, + pub deployment: SubgraphDeployment, + pub block: BlockPointer, + pub proof_of_indexing: Bytes32, +} + +impl PartialEq for ProofOfIndexing { + fn eq(&self, other: &Self) -> bool { + self.indexer.as_ref() == other.indexer.as_ref() + && self.deployment == other.deployment + && self.block == other.block + && self.proof_of_indexing == other.proof_of_indexing + } +} + +impl From for graphix_common_types::ProofOfIndexing { + fn from(value: ProofOfIndexing) -> Self { + todo!() + } +} + +pub trait WritablePoi { + type IndexerId: IndexerId; + + fn deployment_cid(&self) -> &str; + fn indexer_id(&self) -> Self::IndexerId; + fn block(&self) -> BlockPointer; + fn proof_of_indexing(&self) -> &[u8]; +} + +impl WritablePoi for ProofOfIndexing { + type IndexerId = Arc; + + fn deployment_cid(&self) -> &str { + self.deployment.as_str() + } + + fn indexer_id(&self) -> Self::IndexerId { + self.indexer.clone() + } + + fn block(&self) -> BlockPointer { + self.block + } + + fn proof_of_indexing(&self) -> &[u8] { + &self.proof_of_indexing.0 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] +pub struct DivergingBlock { + pub block: BlockPointer, + pub proof_of_indexing1: Bytes32, + pub proof_of_indexing2: Bytes32, +} + +#[derive(Clone, Debug)] +pub struct POICrossCheckReport { + pub poi1: ProofOfIndexing, + pub poi2: ProofOfIndexing, + pub diverging_block: Option, +} + +#[derive(Debug, Clone)] +pub struct PoiRequest { + pub deployment: SubgraphDeployment, + pub block_number: u64, +} diff --git a/backend/crates/common/src/indexer/real_indexer.rs b/crates/indexer-client/src/real_indexer.rs similarity index 79% rename from backend/crates/common/src/indexer/real_indexer.rs rename to crates/indexer-client/src/real_indexer.rs index f58151d..8b26b1e 100644 --- a/backend/crates/common/src/indexer/real_indexer.rs +++ b/crates/indexer-client/src/real_indexer.rs @@ -1,50 +1,45 @@ +use std::borrow::Cow; use std::collections::HashMap; use std::sync::Arc; -use anyhow::anyhow; +use anyhow::{anyhow, Context}; use async_trait::async_trait; use graphql_client::{GraphQLQuery, Response}; -use reqwest::IntoUrl; use serde::de::DeserializeOwned; use serde::Serialize; use tracing::*; use super::{CachedEthereumCall, EntityChanges, Indexer}; -use crate::config::IndexerUrls; -use crate::prelude::{IndexerConfig, WithIndexer}; -use crate::prometheus_metrics::metrics; -use crate::types::{BlockPointer, IndexingStatus, PoiRequest, ProofOfIndexing, SubgraphDeployment}; +use crate::{IndexerId, IndexerVersion, IndexingStatus, PoiRequest, ProofOfIndexing, WithIndexer}; const REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30); #[derive(Debug)] pub struct RealIndexer { - id: String, // Assumed to be unique accross all indexers - address: Option>, - urls: IndexerUrls, + address: Vec, + name: Option, + endpoint: String, client: reqwest::Client, + // Metrics + // ------- + public_poi_requests: prometheus::IntCounterVec, } impl RealIndexer { + // FIXME: this logic is bug-prone and should be replaced with proper sum types. #[instrument(skip_all)] - pub fn new(config: IndexerConfig) -> Self { + pub fn new( + name: Option, + address: Vec, + endpoint: String, + public_poi_requests: prometheus::IntCounterVec, + ) -> Self { Self { - id: config.name, - address: None, - urls: config.urls, - client: reqwest::Client::new(), - } - } - - #[instrument(skip_all)] - pub fn with_address(address: &[u8], status_url: impl IntoUrl) -> Self { - Self { - id: hex::encode(address), - address: Some(address.to_vec()), - urls: IndexerUrls { - status: status_url.into_url().unwrap(), - }, + name, + address, + endpoint, client: reqwest::Client::new(), + public_poi_requests, } } @@ -58,7 +53,7 @@ impl RealIndexer { ) -> anyhow::Result { let response_raw = self .client - .post(self.urls.status.clone()) + .post(self.endpoint.clone()) .timeout(REQUEST_TIMEOUT) .json(&request) .send() @@ -76,8 +71,7 @@ impl RealIndexer { return Err(anyhow::anyhow!("Indexer returned errors: {}", errors)); } - // Unwrap: `data` is always present if there are no errors. - Ok(response.data.unwrap()) + response.data.context("Indexer returned no data") } async fn proofs_of_indexing_batch( @@ -89,7 +83,7 @@ impl RealIndexer { }; let request = gql_types::ProofsOfIndexing::build_query(Variables { requests: requests - .into_iter() + .iter() .map(|query| PublicProofOfIndexingRequest { deployment: query.deployment.to_string(), block_number: query.block_number.to_string(), @@ -110,12 +104,15 @@ impl RealIndexer { #[async_trait] impl Indexer for RealIndexer { - fn id(&self) -> &str { - &self.id + fn address(&self) -> &[u8] { + self.address.as_slice() } - fn address(&self) -> Option<&[u8]> { - self.address.as_deref() + fn name(&self) -> Option> { + match &self.name { + Some(name) => Some(Cow::Borrowed(name.as_str())), + None => None, + } } async fn ping(self: Arc) -> anyhow::Result<()> { @@ -139,7 +136,7 @@ impl Indexer for RealIndexer { Ok(status) => statuses.push(status), Err(e) => { warn!( - id = %self.id(), + address = %self.address_string(), %e, %deployment, "Failed to parse indexing status, skipping deployment" @@ -165,7 +162,7 @@ impl Indexer for RealIndexer { // reduces the impact of this issue. for requests in requests.chunks(1) { trace!( - indexer = %self.id(), + indexer = %self.address_string(), batch_size = requests.len(), "Requesting public Pois batch" ); @@ -174,23 +171,21 @@ impl Indexer for RealIndexer { match result { Ok(batch_pois) => { - metrics() - .public_proofs_of_indexing_requests - .get_metric_with_label_values(&[self.id(), "1"]) + self.public_poi_requests + .get_metric_with_label_values(&[&self.address_string(), "1"]) .unwrap() .inc(); pois.extend(batch_pois); } Err(error) => { - metrics() - .public_proofs_of_indexing_requests - .get_metric_with_label_values(&[self.id(), "0"]) + self.public_poi_requests + .get_metric_with_label_values(&[&self.address_string(), "0"]) .unwrap() .inc(); - warn!( - id = %self.id(), %error, + debug!( + id = %self.address_string(), %error, "Failed to query POIs batch from indexer" ); @@ -198,8 +193,8 @@ impl Indexer for RealIndexer { .to_string() .contains(r#"Cannot query field "publicProofsOfIndexing" on type "Query""#) { - warn!( - id = %self.id(), + debug!( + id = %self.address_string(), "Indexer doesn't seem to support 'publicProofsOfIndexing', skipping it" ); break; @@ -211,6 +206,38 @@ impl Indexer for RealIndexer { pois } + async fn subgraph_api_versions( + self: Arc, + subgraph_id: &str, + ) -> anyhow::Result> { + let request = gql_types::SubgraphApiVersions::build_query( + gql_types::subgraph_api_versions::Variables { + subgraph_id: subgraph_id.to_string(), + }, + ); + + let response: gql_types::subgraph_api_versions::ResponseData = + self.graphql_query(request).await?; + + Ok(response + .api_versions + .into_iter() + .map(|v| v.version) + .collect()) + } + + async fn version(self: Arc) -> anyhow::Result { + let request = gql_types::IndexerVersion::build_query(gql_types::indexer_version::Variables); + + let response: gql_types::indexer_version::ResponseData = + self.graphql_query(request).await?; + + Ok(IndexerVersion { + version: response.version.version, + commit: response.version.commit, + }) + } + async fn cached_eth_calls( self: Arc, network: &str, @@ -228,7 +255,7 @@ impl Indexer for RealIndexer { let eth_calls = response .cached_ethereum_calls - .unwrap_or(vec![]) + .unwrap_or_default() .into_iter() .map(|eth_call| { Ok(CachedEthereumCall { @@ -292,7 +319,7 @@ impl Indexer for RealIndexer { mod gql_types { use super::*; - use crate::prelude::WithIndexer; + use crate::{BlockPointer, SubgraphDeployment}; pub type JSONObject = serde_json::Value; pub type BigInt = String; @@ -333,7 +360,7 @@ mod gql_types { let chain = self .inner .chains - .get(0) + .first() .ok_or_else(|| anyhow!("chain status missing"))?; let (latest_block, earliest_block_num) = match &chain.on { @@ -397,6 +424,24 @@ mod gql_types { } } + #[derive(GraphQLQuery)] + #[graphql( + schema_path = "graphql/indexer/schema.gql", + query_path = "graphql/indexer/queries/indexer-version.gql", + response_derives = "Debug", + variables_derives = "Debug" + )] + pub struct IndexerVersion; + + #[derive(GraphQLQuery)] + #[graphql( + schema_path = "graphql/indexer/schema.gql", + query_path = "graphql/indexer/queries/subgraph-api-versions.gql", + response_derives = "Debug", + variables_derives = "Debug" + )] + pub struct SubgraphApiVersions; + #[derive(GraphQLQuery)] #[graphql( schema_path = "graphql/indexer/schema.gql", diff --git a/crates/network-sg-client/Cargo.toml b/crates/network-sg-client/Cargo.toml new file mode 100644 index 0000000..4ca1c3b --- /dev/null +++ b/crates/network-sg-client/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "graphix_network_sg_client" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +graphix_indexer_client = { path = "../indexer-client" } +hex = "0.4" +prometheus = { version = "0.13", default-features = false } +reqwest = "0.11" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tracing = "0.1" +url = "2" + +[dev-dependencies] +tokio = { version = "1", features = ["macros"] } diff --git a/crates/network-sg-client/src/lib.rs b/crates/network-sg-client/src/lib.rs new file mode 100644 index 0000000..55f64c8 --- /dev/null +++ b/crates/network-sg-client/src/lib.rs @@ -0,0 +1,399 @@ +#![allow(dead_code)] + +use std::collections::BTreeMap; +use std::sync::Arc; +use std::time::Duration; + +use anyhow::anyhow; +use graphix_indexer_client::{Indexer as IndexerTrait, RealIndexer}; +use prometheus::IntCounterVec; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use tracing::warn; +use url::Url; + +/// A GraphQL client that can query the network subgraph and extract useful +/// data. +/// +/// The queries available for this client are oriented towards the needs of +/// Graphix, namely to find high-quality and important indexers and subgraph +/// deployments. +#[derive(Debug, Clone)] +pub struct NetworkSubgraphClient { + endpoint: Url, + timeout: Duration, + client: reqwest::Client, + // Metrics + // ------- + public_poi_requests: IntCounterVec, +} + +impl NetworkSubgraphClient { + const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60); + + /// Creates a new [`NetworkSubgraphClient`] with the given endpoint. + pub fn new(endpoint: Url, public_poi_requests: IntCounterVec) -> Self { + Self { + endpoint, + timeout: Self::DEFAULT_TIMEOUT, + client: reqwest::Client::new(), + public_poi_requests, + } + } + + /// Sets the timeout for requests to the network subgraph. + pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self { + self.timeout = timeout; + self + } + + pub async fn indexers_by_staked_tokens(&self) -> anyhow::Result>> { + let response_data: GraphqlResponseTopIndexers = self + .graphql_query_no_errors( + queries::INDEXERS_BY_STAKED_TOKENS_QUERY, + vec![], + "error(s) querying top indexers from the network subgraph", + ) + .await?; + + let mut indexers: Vec> = vec![]; + for indexer in response_data.indexers { + let indexer_id = indexer.id.clone(); + let real_indexer = indexer_allocation_data_to_real_indexer( + IndexerAllocation { indexer }, + self.public_poi_requests.clone(), + ); + + match real_indexer { + Ok(indexer) => indexers.push(Arc::new(indexer)), + Err(e) => warn!( + err = %e.to_string(), + indexer_id, + "Received bad indexer for network subgraph query; ignoring", + ), + } + } + + Ok(indexers) + } + + pub async fn indexers_by_allocations( + &self, + limit: Option, + ) -> anyhow::Result>> { + let page_size = 100; + + let mut indexers = Vec::>::new(); + loop { + let response_data: GraphqlResponseTopIndexers = self + .graphql_query_no_errors( + queries::INDEXERS_BY_ALLOCATIONS_QUERY, + vec![ + ("first".to_string(), page_size.into()), + ("skip".to_string(), indexers.len().into()), + ], + "error(s) querying indexers by allocations from the network subgraph", + ) + .await?; + + // If we got less than the page size, we're done. + let no_more_results = response_data.indexers.len() < page_size; + + for indexer in response_data.indexers { + if let Some(url) = indexer.url { + let address = hex::decode(indexer.id.trim_start_matches("0x"))?; + let real_indexer = RealIndexer::new( + indexer.default_display_name, + address, + Url::parse(&format!("{}/status", url))?.to_string(), + self.public_poi_requests.clone(), + ); + indexers.push(Arc::new(real_indexer)); + } + } + + if no_more_results { + break; + } + if let Some(limit) = limit { + if indexers.len() > limit as usize { + indexers.truncate(limit as usize); + break; + } + } + } + + Ok(indexers) + } + + /// Instantiates a [`RealIndexer`] from the indexer with the given address, + /// querying the necessary information from the network subgraph. + pub async fn indexer_by_address( + &self, + address: &[u8], + ) -> anyhow::Result> { + let hex_encoded_addr_json = serde_json::to_value(format!("0x{}", hex::encode(address))) + .expect("Unable to hex encode address"); + let response_data: ResponseData = self + .graphql_query_no_errors( + queries::INDEXER_BY_ADDRESS_QUERY, + vec![("id".to_string(), hex_encoded_addr_json)], + "error(s) querying indexer by address from the network subgraph", + ) + .await?; + + #[derive(Debug, Deserialize)] + #[serde(rename_all = "camelCase")] + struct ResponseData { + indexers: Vec, + } + + #[derive(Debug, Deserialize)] + #[serde(rename_all = "camelCase")] + struct IndexerData { + url: String, + default_display_name: String, + } + + let indexer_data = response_data.indexers.first().ok_or_else(|| { + anyhow::anyhow!("No indexer found for address 0x{}", hex::encode(address)) + })?; + + let indexer = RealIndexer::new( + Some(indexer_data.default_display_name.clone()), + address.to_vec(), + Url::parse(&format!("{}/status", indexer_data.url))?.to_string(), + self.public_poi_requests.clone(), + ); + + Ok(Arc::new(indexer)) + } + + /// Returns all subgraph deployments, ordered by curation signal amounts. + pub async fn subgraph_deployments_by_signal( + &self, + limit: Option, + ) -> anyhow::Result> { + let page_size = 100; + + let mut subgraph_deployments = vec![]; + loop { + let response_data: GraphqlResponseSgDeployments = self + .graphql_query_no_errors( + queries::DEPLOYMENTS_QUERY, + vec![ + ("first".to_string(), page_size.into()), + ("skip".to_string(), subgraph_deployments.len().into()), + ], + "error(s) querying deployments from the network subgraph", + ) + .await?; + + // If we got less than the page size, we're done. + let no_more_results = response_data.subgraph_deployments.len() < page_size; + + subgraph_deployments.extend(response_data.subgraph_deployments); + + if no_more_results { + break; + } + if let Some(limit) = limit { + if subgraph_deployments.len() > limit as usize { + subgraph_deployments.truncate(limit as usize); + break; + } + } + } + + Ok(subgraph_deployments) + } + + /// A wrapper around [`NetworkSubgraphClient::graphql_query`] that requires + /// no errors in the response, and deserializes the response data into the + /// given type. + async fn graphql_query_no_errors( + &self, + query: impl ToString, + variables: Vec<(String, serde_json::Value)>, + err_msg: &str, + ) -> anyhow::Result { + let response = self.graphql_query(query, variables).await?; + let response_data = response.data.ok_or_else(|| { + anyhow::anyhow!( + "{}: {}", + err_msg, + serde_json::to_string_pretty(&response.errors.unwrap_or_default()) + .expect("Unable to encode query errors") + ) + })?; + + Ok(serde_json::from_value(response_data)?) + } + + /// Sends a generic GraphQL query to the network subgraph. + pub async fn graphql_query( + &self, + query: impl ToString, + variables: Vec<(String, serde_json::Value)>, + ) -> anyhow::Result { + let request = GraphqlRequest { + query: query.to_string(), + variables: BTreeMap::from_iter(variables), + }; + + tracing::trace!(timeout = ?self.timeout, endpoint = %self.endpoint, "Sending GraphQL request"); + + Ok(self + .client + .post(self.endpoint.as_str()) + .json(&request) + .timeout(self.timeout) + .send() + .await? + .error_for_status()? + .json() + .await?) + } +} + +fn indexer_allocation_data_to_real_indexer( + indexer_allocation: IndexerAllocation, + public_poi_requests: IntCounterVec, +) -> anyhow::Result { + let name = indexer_allocation.indexer.default_display_name.clone(); + let indexer = indexer_allocation.indexer; + let address = hex::decode(indexer.id.trim_start_matches("0x"))?; + let mut url: Url = indexer + .url + .ok_or_else(|| anyhow!("Indexer without URL"))? + .parse()?; + url.set_path("/status"); + Ok(RealIndexer::new( + name, + address, + url.to_string(), + public_poi_requests, + )) +} + +#[derive(Serialize)] +struct GraphqlRequest { + query: String, + variables: BTreeMap, +} + +/// A generic GraphQL response. +#[derive(Deserialize)] +pub struct GraphqlResponse { + /// The response data. + pub data: Option, + /// The response error data. + pub errors: Option>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GraphqlResponseSgDeployments { + subgraph_deployments: Vec, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GraphqlResponseTopIndexers { + indexers: Vec, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct SubgraphDeploymentWithAllocations { + pub ipfs_hash: String, + pub indexer_allocations: Vec, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct IndexerAllocation { + pub indexer: Indexer, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Indexer { + pub id: String, + pub default_display_name: Option, + pub url: Option, +} + +mod queries { + pub const INDEXERS_BY_STAKED_TOKENS_QUERY: &str = + include_str!("queries/indexers_by_staked_tokens.graphql"); + pub const INDEXERS_BY_ALLOCATIONS_QUERY: &str = + include_str!("queries/indexers_by_allocations.graphql"); + pub const DEPLOYMENTS_QUERY: &str = include_str!("queries/deployments.graphql"); + pub const INDEXER_BY_ADDRESS_QUERY: &str = include_str!("queries/indexer_by_address.graphql"); +} + +#[cfg(test)] +mod tests { + use super::*; + + fn network_sg_client_on_ethereum() -> NetworkSubgraphClient { + NetworkSubgraphClient::new( + "https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet" + .parse() + .unwrap(), + IntCounterVec::new(prometheus::Opts::new("foo", "bar"), &["a", "b"]).unwrap(), + ) + } + + #[tokio::test] + async fn short_timeout_always_fails() { + // We should never be able to get a response back under 1ms. If we do, + // it means the timeout logic is broken. + let client = network_sg_client_on_ethereum().with_timeout(Duration::from_millis(1)); + assert!(client.indexers_by_staked_tokens().await.is_err()) + } + + #[tokio::test] + async fn mainnet_indexers_by_staked_tokens_no_panic() { + let client = network_sg_client_on_ethereum(); + let indexers = client.indexers_by_staked_tokens().await.unwrap(); + assert!(!indexers.is_empty()); + } + + #[tokio::test] + async fn mainnet_indexers_by_allocations_no_panic() { + let client = network_sg_client_on_ethereum(); + let indexers = client.indexers_by_allocations(Some(10)).await.unwrap(); + assert_eq!(indexers.len(), 10); + } + + #[tokio::test] + async fn subgraph_deployments_limits() { + let client = network_sg_client_on_ethereum(); + + // Single page. + let deployments = client + .subgraph_deployments_by_signal(Some(5)) + .await + .unwrap(); + assert_eq!(deployments.len(), 5); + + // Muliple pages. + let deployments = client + .subgraph_deployments_by_signal(Some(150)) + .await + .unwrap(); + assert_eq!(deployments.len(), 150); + } + + #[tokio::test] + async fn mainnet_fetch_ellipfra() { + let client = network_sg_client_on_ethereum(); + // ellipfra.eth: + // htps://thegraph.com/explorer/profile/0x62a0bd1d110ff4e5b793119e95fc07c9d1fc8c4a?view=Indexing&chain=mainnet + let address = hex::decode("62a0bd1d110ff4e5b793119e95fc07c9d1fc8c4a").unwrap(); + let indexer = client.indexer_by_address(&address).await.unwrap(); + assert_eq!(indexer.address(), address); + } +} diff --git a/crates/network-sg-client/src/queries/deployments.graphql b/crates/network-sg-client/src/queries/deployments.graphql new file mode 100644 index 0000000..600401f --- /dev/null +++ b/crates/network-sg-client/src/queries/deployments.graphql @@ -0,0 +1,28 @@ +query subgraphDeployments($first: Int!, $skip: Int!) { + subgraphDeployments( + where: { indexerAllocations_: { status_in: [Active] } } + first: $first + skip: $skip + orderBy: signalAmount + orderDirection: desc + ) { + ipfsHash + id + manifest + signalAmount + indexerAllocations( + # Only the 5 indexers with the largest allocations, otherwise + # the query response becomes massive. + first: 5 + orderBy: allocatedTokens + orderDirection: desc + ) { + indexer { + defaultDisplayName + stakedTokens + id + url + } + } + } +} diff --git a/backend/crates/common/src/network_subgraph/queries/indexer_by_address.graphql b/crates/network-sg-client/src/queries/indexer_by_address.graphql similarity index 64% rename from backend/crates/common/src/network_subgraph/queries/indexer_by_address.graphql rename to crates/network-sg-client/src/queries/indexer_by_address.graphql index 570b8f4..ebf3487 100644 --- a/backend/crates/common/src/network_subgraph/queries/indexer_by_address.graphql +++ b/crates/network-sg-client/src/queries/indexer_by_address.graphql @@ -1,4 +1,4 @@ -{ +query IndexerByAddress($id: String) { indexers(where: { id: $id }) { url defaultDisplayName diff --git a/crates/network-sg-client/src/queries/indexers_by_allocations.graphql b/crates/network-sg-client/src/queries/indexers_by_allocations.graphql new file mode 100644 index 0000000..b2f5b48 --- /dev/null +++ b/crates/network-sg-client/src/queries/indexers_by_allocations.graphql @@ -0,0 +1,13 @@ +query IndexersByAllocations($first: Int, $skip: Int) { + indexers( + orderBy: allocatedTokens + orderDirection: desc + first: $first + skip: $skip + ) { + id + defaultDisplayName + url + allocatedTokens + } +} diff --git a/backend/crates/common/src/network_subgraph/queries/indexers_by_staked_tokens.graphql b/crates/network-sg-client/src/queries/indexers_by_staked_tokens.graphql similarity index 64% rename from backend/crates/common/src/network_subgraph/queries/indexers_by_staked_tokens.graphql rename to crates/network-sg-client/src/queries/indexers_by_staked_tokens.graphql index d668c40..c515b62 100644 --- a/backend/crates/common/src/network_subgraph/queries/indexers_by_staked_tokens.graphql +++ b/crates/network-sg-client/src/queries/indexers_by_staked_tokens.graphql @@ -1,4 +1,4 @@ -{ +query IndexersByStakedTokens { indexers(orderBy: stakedTokens) { id url diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml new file mode 100644 index 0000000..5e624c9 --- /dev/null +++ b/crates/store/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "graphix_store" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +async-graphql = "7" +chrono = "0.4" +diesel = { version = "2", features = ["postgres", "r2d2", "chrono", "uuid", "extras"] } +diesel_migrations = { version = "2", features = ["postgres"] } +graphix_common_types = { path = "../common-types" } +graphix_indexer_client = { path = "../indexer-client" } +hex = "0.4" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tracing = "0.1" +uuid = { version = "1", features = ["v4"] } + +[dev-dependencies] +testcontainers = "0.15" +testcontainers-modules = { version = "0.3", features = ["postgres"] } +tokio = { version = "1", features = ["macros"] } diff --git a/backend/crates/common/diesel.toml b/crates/store/diesel.toml similarity index 100% rename from backend/crates/common/diesel.toml rename to crates/store/diesel.toml diff --git a/backend/crates/common/migrations/00000000000000_diesel_initial_setup/down.sql b/crates/store/migrations/00000000000000_diesel_initial_setup/down.sql similarity index 100% rename from backend/crates/common/migrations/00000000000000_diesel_initial_setup/down.sql rename to crates/store/migrations/00000000000000_diesel_initial_setup/down.sql diff --git a/backend/crates/common/migrations/00000000000000_diesel_initial_setup/up.sql b/crates/store/migrations/00000000000000_diesel_initial_setup/up.sql similarity index 100% rename from backend/crates/common/migrations/00000000000000_diesel_initial_setup/up.sql rename to crates/store/migrations/00000000000000_diesel_initial_setup/up.sql diff --git a/backend/crates/common/migrations/2023-03-30-000000_initial_schema/down.sql b/crates/store/migrations/2023-03-30-000000_initial_schema/down.sql similarity index 100% rename from backend/crates/common/migrations/2023-03-30-000000_initial_schema/down.sql rename to crates/store/migrations/2023-03-30-000000_initial_schema/down.sql diff --git a/backend/crates/common/migrations/2023-03-30-000000_initial_schema/up.sql b/crates/store/migrations/2023-03-30-000000_initial_schema/up.sql similarity index 100% rename from backend/crates/common/migrations/2023-03-30-000000_initial_schema/up.sql rename to crates/store/migrations/2023-03-30-000000_initial_schema/up.sql diff --git a/backend/crates/common/migrations/2023-05-26-125000_caip2/down.sql b/crates/store/migrations/2023-05-26-125000_caip2/down.sql similarity index 100% rename from backend/crates/common/migrations/2023-05-26-125000_caip2/down.sql rename to crates/store/migrations/2023-05-26-125000_caip2/down.sql diff --git a/backend/crates/common/migrations/2023-05-26-125000_caip2/up.sql b/crates/store/migrations/2023-05-26-125000_caip2/up.sql similarity index 100% rename from backend/crates/common/migrations/2023-05-26-125000_caip2/up.sql rename to crates/store/migrations/2023-05-26-125000_caip2/up.sql diff --git a/backend/crates/common/migrations/2023-10-09-122825_cleanup/down.sql b/crates/store/migrations/2023-10-09-122825_cleanup/down.sql similarity index 100% rename from backend/crates/common/migrations/2023-10-09-122825_cleanup/down.sql rename to crates/store/migrations/2023-10-09-122825_cleanup/down.sql diff --git a/backend/crates/common/migrations/2023-10-09-122825_cleanup/up.sql b/crates/store/migrations/2023-10-09-122825_cleanup/up.sql similarity index 100% rename from backend/crates/common/migrations/2023-10-09-122825_cleanup/up.sql rename to crates/store/migrations/2023-10-09-122825_cleanup/up.sql diff --git a/crates/store/migrations/2023-12-04-133234_versions/down.sql b/crates/store/migrations/2023-12-04-133234_versions/down.sql new file mode 100644 index 0000000..2b990c5 --- /dev/null +++ b/crates/store/migrations/2023-12-04-133234_versions/down.sql @@ -0,0 +1,3 @@ +DROP TABLE indexer_versions; + +DROP TABLE sg_deployment_api_versions; diff --git a/crates/store/migrations/2023-12-04-133234_versions/up.sql b/crates/store/migrations/2023-12-04-133234_versions/up.sql new file mode 100644 index 0000000..c164328 --- /dev/null +++ b/crates/store/migrations/2023-12-04-133234_versions/up.sql @@ -0,0 +1,20 @@ +CREATE TABLE indexer_versions ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + indexer_id INTEGER NOT NULL REFERENCES indexers(id) ON DELETE CASCADE, + error TEXT, + version_string TEXT, + version_commit TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE INDEX indexer_versions_indexer_id_idx ON indexer_versions(indexer_id); + +CREATE TABLE sg_deployment_api_versions ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + sg_deployment_id INTEGER NOT NULL REFERENCES sg_deployments(id) ON DELETE CASCADE, + api_versions TEXT[] DEFAULT '{}', + error TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE INDEX sg_deployment_api_versions_sg_deployment_id_idx ON sg_deployment_api_versions(sg_deployment_id); diff --git a/crates/store/migrations/2024-01-22-122801_non_null_address/down.sql b/crates/store/migrations/2024-01-22-122801_non_null_address/down.sql new file mode 100644 index 0000000..10effc8 --- /dev/null +++ b/crates/store/migrations/2024-01-22-122801_non_null_address/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE + indexers +ALTER COLUMN + address DROP NOT NULL; diff --git a/crates/store/migrations/2024-01-22-122801_non_null_address/up.sql b/crates/store/migrations/2024-01-22-122801_non_null_address/up.sql new file mode 100644 index 0000000..e712116 --- /dev/null +++ b/crates/store/migrations/2024-01-22-122801_non_null_address/up.sql @@ -0,0 +1,6 @@ +ALTER TABLE + indexers +ALTER COLUMN + address +SET + NOT NULL; diff --git a/backend/crates/common/src/store/diesel_queries.rs b/crates/store/src/diesel_queries.rs similarity index 77% rename from backend/crates/common/src/store/diesel_queries.rs rename to crates/store/src/diesel_queries.rs index 24343c4..0de48d8 100644 --- a/backend/crates/common/src/store/diesel_queries.rs +++ b/crates/store/src/diesel_queries.rs @@ -1,18 +1,21 @@ +//! Provides the diesel queries, callers should handle connection pooling and +//! transactions. + +use std::borrow::Cow; use std::collections::BTreeMap; use chrono::Utc; use diesel::prelude::*; use diesel::sql_types; +use graphix_common_types::{BlockRangeInput, IndexerVersion}; +use graphix_indexer_client::{BlockPointer, Indexer, IndexerId, WritablePoi}; use tracing::info; -use super::models::WritablePoi; use super::PoiLiveness; -use crate::graphql_api::types::BlockRangeInput; -use crate::prelude::BlockPointer; -use crate::store::models::{ - self, IndexerRow, NewIndexer, NewLivePoi, NewPoi, NewSgDeployment, SgDeployment, +use crate::models::{ + self, Indexer as IndexerModel, NewIndexer, NewLivePoi, NewPoi, NewSgDeployment, SgDeployment, }; -use crate::store::schema::{self, live_pois}; +use crate::schema::{self, live_pois}; pub(super) fn poi(conn: &mut PgConnection, poi: &str) -> anyhow::Result> { use schema::{blocks, indexers, pois, sg_deployments}; @@ -82,7 +85,7 @@ pub(super) fn pois( }; let order_by = (blocks::number.desc(), schema::pois::created_at.desc()); - let limit = limit.map(|l| l as i64).unwrap_or(i64::MAX) as i64; + let limit = limit.map(|l| l as i64).unwrap_or(i64::MAX); match live_only { false => { @@ -96,7 +99,7 @@ pub(super) fn pois( .filter(blocks_filter) .filter(indexer_filter) .limit(limit); - return Ok(query.load::(conn)?); + Ok(query.load::(conn)?) } // This will additionally join with `live_pois` to filter out any Pois that are not live. true => { @@ -111,11 +114,36 @@ pub(super) fn pois( .filter(blocks_filter) .filter(indexer_filter) .limit(limit); - return Ok(query.load::(conn)?); + Ok(query.load::(conn)?) } } } +pub fn write_indexers( + conn: &mut PgConnection, + indexers: &[impl AsRef], +) -> anyhow::Result<()> { + use schema::indexers; + + let insertable_indexers = indexers + .iter() + .map(|indexer| { + let indexer = indexer.as_ref(); + NewIndexer { + address: indexer.address().to_owned(), + name: indexer.name().map(|s| s.to_string()), + } + }) + .collect::>(); + + diesel::insert_into(indexers::table) + .values(insertable_indexers) + .on_conflict_do_nothing() + .execute(conn)?; + + Ok(()) +} + pub fn set_deployment_name( conn: &mut PgConnection, sg_deployment_id: &str, @@ -186,7 +214,7 @@ pub(super) fn write_pois( .iter() .map(|poi| { let indexer_id = - get_or_insert_indexer(conn, poi.indexer_id(), poi.indexer_address())?; + get_indexer_id(conn, poi.indexer_id().name(), poi.indexer_id().address())?; Ok(NewPoi { sg_deployment_id, @@ -256,32 +284,59 @@ fn get_or_insert_block(conn: &mut PgConnection, block: BlockPointer) -> anyhow:: } } -fn get_or_insert_indexer( +pub fn write_graph_node_version( conn: &mut PgConnection, - id: &str, - address: Option<&[u8]>, -) -> Result { + indexer: &dyn Indexer, + version: anyhow::Result, +) -> anyhow::Result<()> { + use schema::indexer_versions; + + let indexer_id = get_indexer_id(conn, indexer.name(), indexer.address())?; + + let new_version = match version { + Ok(v) => models::NewIndexerVersion { + indexer_id, + error: None, + version_string: Some(v.version), + version_commit: Some(v.commit), + }, + Err(err) => models::NewIndexerVersion { + indexer_id, + error: Some(err.to_string()), + version_string: None, + version_commit: None, + }, + }; + + diesel::insert_into(indexer_versions::table) + .values(&new_version) + .execute(conn)?; + + Ok(()) +} + +fn get_indexer_id( + conn: &mut PgConnection, + name: Option>, + address: &[u8], +) -> anyhow::Result { use schema::indexers; - let existing_indexer: Option = indexers::table - .filter(indexers::name.is_not_distinct_from(id)) + let existing_indexer: Option = indexers::table + .filter(indexers::name.is_not_distinct_from(&name)) .filter(indexers::address.is_not_distinct_from(address)) .get_result(conn) .optional()?; - Ok(if let Some(existing_indexer) = existing_indexer { - // If the indexer exists, use its id - existing_indexer.id + + if let Some(i) = existing_indexer { + Ok(i.id) } else { - // If the indexer doesn't exist, insert a new one and return its id - let new_indexer = NewIndexer { - address: address.map(ToOwned::to_owned), - name: Some(id.to_string()), - }; - diesel::insert_into(indexers::table) - .values(&new_indexer) - .returning(indexers::id) - .get_result(conn)? - }) + Err(anyhow::anyhow!( + "Indexer with name {:?} and/or address {:?} not found", + &name, + address + )) + } } fn get_or_insert_deployment( diff --git a/backend/crates/common/src/store/mod.rs b/crates/store/src/lib.rs similarity index 83% rename from backend/crates/common/src/store/mod.rs rename to crates/store/src/lib.rs index 50c73a8..d440e31 100644 --- a/backend/crates/common/src/store/mod.rs +++ b/crates/store/src/lib.rs @@ -1,28 +1,31 @@ -//! Database access (read and write) abstractions for all Graphix backend -//! services. +//! Database access (read and write) abstractions for the Graphix backend. + +mod diesel_queries; +#[cfg(tests)] +pub use diesel_queries; +use graphix_common_types::{ + BlockRangeInput, IndexerVersion, IndexersQuery, Network, SgDeploymentsQuery, +}; +pub mod models; +mod schema; + +use std::collections::HashMap; +use std::sync::Arc; use anyhow::Error; use diesel::prelude::*; use diesel::r2d2::{self, ConnectionManager, Pool, PooledConnection}; use diesel::{Connection, PgConnection}; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; +use graphix_indexer_client::{Indexer, WritablePoi}; use tracing::info; -use crate::graphql_api::types::{BlockRangeInput, IndexersQuery, SgDeploymentsQuery}; -use crate::store::models::{IndexerRow, Poi}; - -// Provides the diesel queries, callers should handle connection pooling and transactions. -mod diesel_queries; -#[cfg(tests)] -pub use diesel_queries; - -use self::models::{QueriedSgDeployment, WritablePoi}; +use self::models::QueriedSgDeployment; +use crate::models::{Indexer as IndexerModel, Poi}; -pub mod models; -mod schema; - -#[cfg(test)] -mod tests; +// TODO +//#[cfg(test)] +//mod tests; const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); @@ -34,7 +37,7 @@ pub struct Store { } impl Store { - /// Connects to the database and runs migrations. + /// Connects to the database and runs all pending migrations. pub async fn new(db_url: &str) -> anyhow::Result { info!("Initializing database connection pool"); let manager = r2d2::ConnectionManager::::new(db_url); @@ -48,6 +51,8 @@ impl Store { let mut conn = self.pool.get()?; // Get a lock for running migrations. Blocks until we get the lock. + // We need this because different Graphix instances may attempt + // to run migrations concurrently (that's a big no-no). diesel::sql_query("select pg_advisory_lock(1)").execute(&mut conn)?; info!("Run database migrations"); conn.run_pending_migrations(MIGRATIONS) @@ -132,12 +137,29 @@ impl Store { diesel_queries::delete_network(&mut conn, network_name) } + pub fn networks(&self) -> anyhow::Result> { + use schema::networks; + + let mut conn = self.conn()?; + let rows = networks::table + .select((networks::name, networks::caip2)) + .load::<(String, Option)>(&mut conn)?; + + let networks = rows + .into_iter() + .map(|(name, caip2)| Network { name, caip2 }) + .collect(); + + Ok(networks) + } + /// Returns all indexers stored in the database. pub fn indexers(&self, filter: IndexersQuery) -> anyhow::Result> { use schema::indexers; let mut query = indexers::table.into_boxed(); + // FIXME if let Some(address) = filter.address { query = query.filter(indexers::name.eq(address)); } @@ -145,7 +167,7 @@ impl Store { query = query.limit(limit.into()); } - let rows = query.load::(&mut self.conn()?)?; + let rows = query.load::(&mut self.conn()?)?; Ok(rows.into_iter().map(Into::into).collect()) } @@ -193,6 +215,24 @@ impl Store { .transaction::<_, Error, _>(|conn| diesel_queries::write_pois(conn, pois, live)) } + pub fn write_indexers(&self, indexers: &[impl AsRef]) -> anyhow::Result<()> { + let mut conn = self.conn()?; + diesel_queries::write_indexers(&mut conn, indexers)?; + Ok(()) + } + + pub fn write_graph_node_versions( + &self, + versions: HashMap, anyhow::Result>, + ) -> anyhow::Result<()> { + for (indexer, version) in versions { + let mut conn = self.conn()?; + diesel_queries::write_graph_node_version(&mut conn, &*indexer, version)?; + } + + Ok(()) + } + pub fn get_first_pending_divergence_investigation_request( &self, ) -> anyhow::Result> { diff --git a/backend/crates/common/src/store/models/mod.rs b/crates/store/src/models.rs similarity index 63% rename from backend/crates/common/src/store/models/mod.rs rename to crates/store/src/models.rs index cc78482..c55d955 100644 --- a/backend/crates/common/src/store/models/mod.rs +++ b/crates/store/src/models.rs @@ -1,14 +1,18 @@ +use std::borrow::Cow; + use async_graphql::SimpleObject; use chrono::NaiveDateTime; +use diesel::backend::Backend; use diesel::deserialize::FromSql; use diesel::pg::Pg; use diesel::sql_types::Jsonb; -use diesel::{backend, AsChangeset, AsExpression, FromSqlRow, Insertable, Queryable}; +use diesel::{AsChangeset, AsExpression, FromSqlRow, Insertable, Queryable}; +use graphix_common_types as types; +use graphix_indexer_client::IndexerId; use serde::{Deserialize, Serialize}; -use types::BlockPointer; +use types::{Deployment, Network}; use super::schema::*; -use crate::types; pub type IntId = i32; pub type BigIntId = i64; @@ -21,7 +25,7 @@ pub struct Poi { #[serde(skip)] pub created_at: NaiveDateTime, pub sg_deployment: SgDeployment, - pub indexer: IndexerRow, + pub indexer: Indexer, pub block: Block, } @@ -31,10 +35,13 @@ impl Poi { } } -#[derive(Debug, Copy, Clone)] -pub enum IndexerRef<'a> { - Id(IntId), - Address(&'a [u8]), +#[derive(Insertable, Debug)] +#[diesel(table_name = indexer_versions)] +pub struct NewIndexerVersion { + pub indexer_id: IntId, + pub error: Option, + pub version_string: Option, + pub version_commit: Option, } #[derive(Insertable, Debug)] @@ -47,14 +54,6 @@ pub struct NewPoi { pub block_id: BigIntId, } -pub trait WritablePoi { - fn deployment_cid(&self) -> &str; - fn indexer_id(&self) -> &str; - fn indexer_address(&self) -> Option<&[u8]>; - fn block(&self) -> BlockPointer; - fn proof_of_indexing(&self) -> &[u8]; -} - #[derive(Queryable, Debug, Serialize)] pub struct Block { pub(super) id: BigIntId, @@ -71,38 +70,32 @@ pub struct NewBlock { pub hash: Vec, } -#[derive(Debug, Queryable)] +#[derive(Debug, Queryable, Serialize)] pub struct Indexer { pub id: IntId, pub name: Option, - pub address: Option>, + pub address: Vec, + #[serde(skip)] pub created_at: NaiveDateTime, } -impl From for Indexer { - fn from(row: IndexerRow) -> Self { - Self { - id: row.id, - name: row.name, - address: row.address, - created_at: row.created_at, - } +impl IndexerId for Indexer { + fn address(&self) -> &[u8] { + self.address.as_slice() } -} -#[derive(Debug, Queryable, Serialize)] -pub struct IndexerRow { - pub id: IntId, - pub name: Option, - pub address: Option>, - #[serde(skip)] - pub created_at: NaiveDateTime, + fn name(&self) -> Option> { + match &self.name { + Some(name) => Some(Cow::Borrowed(name)), + None => None, + } + } } #[derive(Debug, Insertable)] #[diesel(table_name = indexers)] pub struct NewIndexer { - pub address: Option>, + pub address: Vec, pub name: Option, } @@ -163,8 +156,42 @@ impl From for DivergingBlock { } impl FromSql for DivergingBlock { - fn from_sql(bytes: backend::RawValue) -> diesel::deserialize::Result { + fn from_sql(bytes: ::RawValue<'_>) -> diesel::deserialize::Result { let value = >::from_sql(bytes)?; Ok(serde_json::from_value(value)?) } } + +impl From for graphix_common_types::Indexer { + fn from(indexer: Indexer) -> Self { + let address_string = indexer.address_string(); + Self { + id: address_string.clone(), + name: indexer.name, + version: None, // TODO + address: Some(address_string), + allocated_tokens: None, // TODO: we don't store this in the db yet + } + } +} + +impl From for types::ProofOfIndexing { + fn from(poi: Poi) -> Self { + Self { + allocated_tokens: None, + deployment: Deployment { + id: poi.sg_deployment.cid.clone(), + }, + hash: poi.poi_hex(), + block: graphix_common_types::Block { + network: Network { + name: "mainnet".to_string(), + caip2: None, + }, + number: poi.block.number as u64, + hash: hex::encode(poi.block.hash), + }, + indexer: poi.indexer.into(), + } + } +} diff --git a/backend/crates/common/src/db/schema.rs b/crates/store/src/schema.rs similarity index 75% rename from backend/crates/common/src/db/schema.rs rename to crates/store/src/schema.rs index 5a9dad0..5d4549b 100644 --- a/backend/crates/common/src/db/schema.rs +++ b/crates/store/src/schema.rs @@ -17,11 +17,22 @@ diesel::table! { } } +diesel::table! { + indexer_versions (id) { + id -> Int4, + indexer_id -> Int4, + error -> Nullable, + version_string -> Nullable, + version_commit -> Nullable, + created_at -> Timestamp, + } +} + diesel::table! { indexers (id) { id -> Int4, name -> Nullable, - address -> Nullable, + address -> Bytea, created_at -> Timestamp, } } @@ -63,6 +74,16 @@ diesel::table! { } } +diesel::table! { + sg_deployment_api_versions (id) { + id -> Int4, + sg_deployment_id -> Int4, + api_versions -> Nullable>>, + error -> Nullable, + created_at -> Timestamp, + } +} + diesel::table! { sg_deployments (id) { id -> Int4, @@ -82,23 +103,27 @@ diesel::table! { } diesel::joinable!(blocks -> networks (network_id)); +diesel::joinable!(indexer_versions -> indexers (indexer_id)); diesel::joinable!(live_pois -> indexers (indexer_id)); diesel::joinable!(live_pois -> pois (poi_id)); diesel::joinable!(live_pois -> sg_deployments (sg_deployment_id)); diesel::joinable!(pois -> blocks (block_id)); diesel::joinable!(pois -> indexers (indexer_id)); diesel::joinable!(pois -> sg_deployments (sg_deployment_id)); +diesel::joinable!(sg_deployment_api_versions -> sg_deployments (sg_deployment_id)); diesel::joinable!(sg_deployments -> networks (network)); diesel::joinable!(sg_names -> sg_deployments (sg_deployment_id)); diesel::allow_tables_to_appear_in_same_query!( blocks, divergence_investigation_reports, + indexer_versions, indexers, live_pois, networks, pending_divergence_investigation_requests, pois, + sg_deployment_api_versions, sg_deployments, sg_names, ); diff --git a/backend/crates/common/src/store/tests.rs b/crates/store/src/tests.rs similarity index 72% rename from backend/crates/common/src/store/tests.rs rename to crates/store/src/tests.rs index edcde0f..d68294b 100644 --- a/backend/crates/common/src/store/tests.rs +++ b/crates/store/src/tests.rs @@ -1,30 +1,55 @@ +use std::collections::BTreeSet; +use std::ops::Deref; + +use diesel::Connection; +use graphix_common_types::SgDeploymentsQuery; +use testcontainers::clients::Cli; +use testcontainers::Container; + use crate::block_choice::BlockChoicePolicy; -use crate::graphql_api::types::SgDeploymentsQuery; -use crate::prelude::ProofOfIndexing; use crate::prometheus_metrics::metrics; -use crate::test_utils::{fast_rng, gen::gen_bytes32, gen::gen_indexers}; -use crate::{ - queries, - store::{diesel_queries, PoiLiveness, Store}, -}; -use diesel::Connection; -use std::collections::BTreeSet; -use std::sync::{Mutex, OnceLock}; +use crate::queries; +use crate::test_utils::fast_rng; +use crate::test_utils::gen::{gen_bytes32, gen_indexers}; +use crate::{diesel_queries, PoiLiveness, Store}; +use graphix_indexer_client::{IndexerId, ProofOfIndexing}; + +/// A wrapper around a [`Store`] that is backed by a containerized Postgres +/// database. +pub struct EmptyStoreForTesting<'a> { + _container: Container<'a, testcontainers_modules::postgres::Postgres>, + store: Store, +} -static TEST_LOCK: OnceLock> = OnceLock::new(); +impl<'a> EmptyStoreForTesting<'a> { + pub async fn new(docker_client: &'a Cli) -> anyhow::Result> { + use testcontainers_modules::postgres::Postgres; -fn test_lock() -> &'static Mutex<()> { - TEST_LOCK.get_or_init(|| Mutex::new(())) + let container = docker_client.run(Postgres::default()); + let connection_string = &format!( + "postgres://postgres:postgres@127.0.0.1:{}/postgres", + container.get_host_port_ipv4(5432) + ); + let store = Store::new(connection_string).await?; + Ok(Self { + _container: container, + store, + }) + } } -fn test_db_url() -> String { - std::env::var("GRAPHIX_TEST_DB_URL").expect("GRAPHIX_TEST_DB_URL must be set to run tests") +impl<'a> Deref for EmptyStoreForTesting<'a> { + type Target = Store; + + fn deref(&self) -> &Self::Target { + &self.store + } } #[tokio::test] -#[ignore] // FIXME: Race condition with other tests async fn no_deployments_at_first() { - let store = Store::new(&test_db_url()).await.unwrap(); + let docker_cli = Cli::default(); + let store = EmptyStoreForTesting::new(&docker_cli).await.unwrap(); let initial_deployments = store.sg_deployments(SgDeploymentsQuery::default()).unwrap(); assert!(initial_deployments.is_empty()); } @@ -32,7 +57,8 @@ async fn no_deployments_at_first() { #[tokio::test] #[should_panic] // FIXME async fn deployments_with_name() { - let store = Store::new(&test_db_url()).await.unwrap(); + let docker_cli = Cli::default(); + let store = EmptyStoreForTesting::new(&docker_cli).await.unwrap(); let ipfs_cid1 = "QmNY7gDNXHECV8SXoEY7hbfg4BX1aDMxTBDiFuG4huaSGA"; let ipfs_cid2 = "QmYzsCjrVwwXtdsNm3PZVNziLGmb9o513GUzkq5wwhgXDT"; @@ -42,8 +68,10 @@ async fn deployments_with_name() { store.set_deployment_name(ipfs_cid2, "foo").unwrap(); let deployments = { - let mut filter = SgDeploymentsQuery::default(); - filter.name = Some("foo".to_string()); + let filter = SgDeploymentsQuery { + name: Some("foo".to_string()), + ..Default::default() + }; store.sg_deployments(filter).unwrap() }; assert!(deployments.len() == 1); @@ -53,7 +81,9 @@ async fn deployments_with_name() { #[tokio::test] async fn create_divergence_investigation_request() { - let store = Store::new(&test_db_url()).await.unwrap(); + let docker_cli = Cli::default(); + let store = EmptyStoreForTesting::new(&docker_cli).await.unwrap(); + let uuid = store .create_divergence_investigation_request(serde_json::json!({})) .unwrap(); @@ -67,7 +97,8 @@ async fn create_divergence_investigation_request() { #[tokio::test] async fn poi_db_roundtrip() { - let _lock = test_lock().lock().unwrap(); + let docker_cli = Cli::default(); + let store = EmptyStoreForTesting::new(&docker_cli).await.unwrap(); let mut rng = fast_rng(0); let indexers = gen_indexers(&mut rng, 100); @@ -76,7 +107,6 @@ async fn poi_db_roundtrip() { let pois = queries::query_proofs_of_indexing(indexing_statuses, BlockChoicePolicy::Earliest).await; - let store = Store::new(&test_db_url()).await.unwrap(); let mut conn = store.conn().unwrap(); conn.test_transaction(|conn| test_pois(conn, &pois, PoiLiveness::NotLive, false)); conn.test_transaction(|conn| test_pois(conn, &pois, PoiLiveness::Live, true)); @@ -84,7 +114,8 @@ async fn poi_db_roundtrip() { #[tokio::test] async fn test_additional_pois() { - let _lock = test_lock().lock().unwrap(); + let docker_cli = Cli::default(); + let store = EmptyStoreForTesting::new(&docker_cli).await.unwrap(); let mut rng = fast_rng(0); let indexers = gen_indexers(&mut rng, 100); @@ -93,7 +124,6 @@ async fn test_additional_pois() { let pois = queries::query_proofs_of_indexing(indexing_statuses, BlockChoicePolicy::Earliest).await; - let store = Store::new(&test_db_url()).await.unwrap(); let mut conn = store.conn().unwrap(); conn.test_transaction(|conn| -> Result<(), anyhow::Error> { @@ -142,12 +172,11 @@ fn test_pois( // Common logic to create poi_triples let poi_triples: BTreeSet<(String, String, Vec)> = pois - .clone() - .into_iter() + .iter() .map(|poi| { ( poi.deployment.0.clone(), - poi.indexer.id().to_owned(), + poi.indexer.address_string(), poi.proof_of_indexing.0.to_vec(), ) }) diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml deleted file mode 100644 index 8763f93..0000000 --- a/frontend/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "graphix-frontend" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow = "1.0.53" -css-colors = "1.0.1" -gloo = "0.6.0" -graphql_client = "0.10.0" -log = "0.4.14" -once_cell = "1.9.0" -reqwasm = "0.4.0" -serde = "1.0.136" -serde_json = "1.0.78" -stylist = { version = "0.10.0", features = ["yew_integration"] } -wasm-bindgen-futures = "0.4.29" -wasm-logger = "0.2.0" -yew = "0.19.3" -yew-router = "0.16.0" - -[build-dependencies] -async-graphql = "3.0.22" -graphix-common = { path = "../backend/crates/common" } diff --git a/frontend/build.rs b/frontend/build.rs deleted file mode 100644 index a67b96e..0000000 --- a/frontend/build.rs +++ /dev/null @@ -1,32 +0,0 @@ -use async_graphql::*; -use graphix_common::api_types::QueryRoot; -use std::env; -use std::fs::File; -use std::io::*; - -struct Query; - -#[Object] -impl Query { - async fn howdy(&self) -> &'static str { - "partner" - } -} - -fn main() -> std::io::Result<()> { - // We're only interested in re-generating the API schema if build - // dependencies change or the build script itself does. - // See . - println!("cargo:rerun-if-changed=build.rs"); - - let api_schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).finish(); - let path = env::current_dir()?.join("graphql/api_schema.graphql"); - - let mut f = File::create(&path)?; - - f.write_all(b"# AUTOGENERATED. DO NOT MODIFY. ALL CHANGES WILL BE LOST.\n\n")?; - f.write_all(api_schema.sdl().as_bytes())?; - - println!("Updated: {}", path.display()); - Ok(()) -} diff --git a/frontend/graphql/queries/deployments.graphql b/frontend/graphql/queries/deployments.graphql deleted file mode 100644 index dc5e461..0000000 --- a/frontend/graphql/queries/deployments.graphql +++ /dev/null @@ -1,3 +0,0 @@ -query Deployments { - deployments -} diff --git a/frontend/graphql/queries/launch_cross_check_report.graphql b/frontend/graphql/queries/launch_cross_check_report.graphql deleted file mode 100644 index 93f3945..0000000 --- a/frontend/graphql/queries/launch_cross_check_report.graphql +++ /dev/null @@ -1,4 +0,0 @@ -mutation LaunchCrossCheckReport() { - # FIXME, TODO - deployments -} diff --git a/frontend/graphql/queries/poi_cross_check_reports.graphql b/frontend/graphql/queries/poi_cross_check_reports.graphql deleted file mode 100644 index b74de43..0000000 --- a/frontend/graphql/queries/poi_cross_check_reports.graphql +++ /dev/null @@ -1,14 +0,0 @@ -query POICrossCheckReports($request: POICrossCheckReportRequest!) { - poiCrossCheckReports(request: $request) { - timestamp - indexer1 - indexer2 - deployment - block { - number - hash - } - proofOfIndexing1 - proofOfIndexing2 - } -} diff --git a/frontend/graphql/queries/proofs_of_indexing.graphql b/frontend/graphql/queries/proofs_of_indexing.graphql deleted file mode 100644 index d31a688..0000000 --- a/frontend/graphql/queries/proofs_of_indexing.graphql +++ /dev/null @@ -1,12 +0,0 @@ -query ProofsOfIndexing($request: ProofOfIndexingRequest!) { - proofsOfIndexing(request: $request) { - timestamp - deployment - indexer - block { - number - hash - } - proofOfIndexing - } -} diff --git a/frontend/index.html b/frontend/index.html deleted file mode 100644 index e567cb1..0000000 --- a/frontend/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - Graphix - - - - - - - - - - diff --git a/frontend/src/components/mod.rs b/frontend/src/components/mod.rs deleted file mode 100644 index 99a67a6..0000000 --- a/frontend/src/components/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod navigation; -mod navigation_menu_item; -mod page; -mod page_header; - -pub use self::navigation::*; -pub use self::navigation_menu_item::*; -pub use self::page::*; -pub use self::page_header::*; diff --git a/frontend/src/components/navigation.rs b/frontend/src/components/navigation.rs deleted file mode 100644 index a29ad9f..0000000 --- a/frontend/src/components/navigation.rs +++ /dev/null @@ -1,40 +0,0 @@ -use stylist::yew::*; -use yew::prelude::*; -use yew_router::prelude::*; - -use super::NavigationMenuItem; -use crate::contexts::use_theme; -use crate::routes::*; - -#[styled_component(Navigation)] -pub fn navigation() -> Html { - let _theme = use_theme(); - - html! { -
- - - - -
- } -} diff --git a/frontend/src/components/navigation_menu_item.rs b/frontend/src/components/navigation_menu_item.rs deleted file mode 100644 index 57b271a..0000000 --- a/frontend/src/components/navigation_menu_item.rs +++ /dev/null @@ -1,28 +0,0 @@ -use stylist::yew::*; -use yew::prelude::*; - -#[derive(Clone, Properties, PartialEq)] -pub struct NavigationMenuItemProps { - pub name: String, - pub fontawesome_icon: String, - pub is_active: bool, -} - -#[styled_component(NavigationMenuItem)] -pub fn navigation_menu_item(props: &NavigationMenuItemProps) -> Html { - let fontawesome_attrs = format!("fa mr-4 fa-{}", props.fontawesome_icon); - let item_attrs = if props.is_active { - "mb-2 bg-gray-800 rounded shadow" - } else { - "mb-2 rounded hover:shadow hover:bg-gray-800" - }; - - html! { -
  • - - - {props.name.as_str()} - -
  • - } -} diff --git a/frontend/src/components/page.rs b/frontend/src/components/page.rs deleted file mode 100644 index 9f5b61a..0000000 --- a/frontend/src/components/page.rs +++ /dev/null @@ -1,23 +0,0 @@ -use stylist::yew::*; -use yew::prelude::*; - -use crate::components::PageHeader; - -#[derive(Properties, PartialEq)] -pub struct PageProps { - pub title: String, - #[prop_or_default] - pub children: Children, -} - -#[styled_component(Page)] -pub fn page(props: &PageProps) -> Html { - html! { -
    - - <> - { for props.children.iter() } - -
    - } -} diff --git a/frontend/src/components/page_header.rs b/frontend/src/components/page_header.rs deleted file mode 100644 index dae37de..0000000 --- a/frontend/src/components/page_header.rs +++ /dev/null @@ -1,22 +0,0 @@ -use stylist::yew::*; -use yew::prelude::*; - -#[derive(Properties, PartialEq)] -pub struct PageHeaderProps { - pub title: String, -} - -#[styled_component(PageHeader)] -pub fn page_header(props: &PageHeaderProps) -> Html { - html! { -
    -

    {&props.title}

    -
    - } -} diff --git a/frontend/src/contexts/mod.rs b/frontend/src/contexts/mod.rs deleted file mode 100644 index 687df2a..0000000 --- a/frontend/src/contexts/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod theme; - -pub use theme::*; diff --git a/frontend/src/contexts/theme.rs b/frontend/src/contexts/theme.rs deleted file mode 100644 index 283f7dc..0000000 --- a/frontend/src/contexts/theme.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::ops::Deref; - -use css_colors::{rgba, Color, Ratio}; -use stylist::yew::styled_component; -use yew::html::ImplicitClone; -use yew::prelude::*; - -#[derive(Debug, Clone, PartialEq)] -pub struct ThemeColors { - pub background: String, - pub text: String, - pub one: String, - pub two: String, - pub three: String, - pub four: String, - pub five: String, - pub link: String, - pub link_hover: String, - pub ok: String, - pub error: String, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Theme { - pub colors: ThemeColors, -} - -impl Default for Theme { - fn default() -> Self { - Self { - colors: ThemeColors { - background: "white".into(), - text: "black".into(), - one: "#1f2041ff".into(), - two: "#4b3f72ff".into(), - three: "#ffc857ff".into(), - four: "#119da4ff".into(), - five: "#19647eff".into(), - link: "black".into(), - link_hover: "#19647eff".into(), - ok: rgba(0, 255, 0, 1.0) - .lighten(Ratio::from_percentage(20)) - .to_css(), - error: rgba(255, 0, 0, 1.0) - .lighten(Ratio::from_percentage(20)) - .to_css(), - }, - } - } -} - -impl ImplicitClone for Theme {} - -#[derive(Debug, Clone)] -pub struct ThemeContext { - inner: UseStateHandle, -} - -impl ThemeContext { - pub fn new(inner: UseStateHandle) -> Self { - Self { inner } - } - - pub fn _set(&self, theme: Theme) { - self.inner.set(theme) - } - - pub fn _get(&self) -> Theme { - (*self.inner).clone() - } -} - -impl Deref for ThemeContext { - type Target = Theme; - - fn deref(&self) -> &Self::Target { - &*self.inner - } -} - -impl PartialEq for ThemeContext { - fn eq(&self, rhs: &Self) -> bool { - *self.inner == *rhs.inner - } -} - -#[derive(Debug, PartialEq, Properties)] -pub struct ThemeProviderProps { - pub children: Children, -} - -#[styled_component(ThemeProvider)] -pub fn theme_provider(props: &ThemeProviderProps) -> Html { - let theme = use_state(Theme::default); - let theme_ctx = ThemeContext::new(theme); - - html! { - context={theme_ctx}> - {props.children.clone()} - > - } -} - -pub fn use_theme() -> ThemeContext { - use_context::().unwrap() -} diff --git a/frontend/src/main.rs b/frontend/src/main.rs deleted file mode 100644 index 90d4ff3..0000000 --- a/frontend/src/main.rs +++ /dev/null @@ -1,45 +0,0 @@ -pub mod components; -mod contexts; -pub mod pages; -mod routes; - -use stylist::yew::*; -use yew::prelude::*; -use yew_router::prelude::*; - -use components::*; -use contexts::*; -use routes::*; - -#[styled_component(App)] -fn app() -> Html { - html! { - -
    -
    - -
    - render={Switch::render(switch)} /> -
    -
    -
    -
    - } -} - -#[styled_component(Root)] -fn root() -> Html { - html! { - - - - } -} - -fn main() { - wasm_logger::init(wasm_logger::Config::default()); - - log::info!("Starting up"); - - yew::start_app::(); -} diff --git a/frontend/src/pages/mod.rs b/frontend/src/pages/mod.rs deleted file mode 100644 index 33e49b5..0000000 --- a/frontend/src/pages/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod poi_explorer; -pub mod poi_reports; diff --git a/frontend/src/pages/poi_explorer/deployment.rs b/frontend/src/pages/poi_explorer/deployment.rs deleted file mode 100644 index a847684..0000000 --- a/frontend/src/pages/poi_explorer/deployment.rs +++ /dev/null @@ -1,311 +0,0 @@ -use std::collections::BTreeMap; - -use css_colors::{rgba, Color, Ratio, RGBA}; -use gloo::timers::callback::Interval; -use graphql_client::{GraphQLQuery, Response as GraphQLResponse}; -use log::warn; -use reqwasm::http::*; -use stylist::css; -use wasm_bindgen_futures::spawn_local; -use yew::{html::Scope, prelude::*}; - -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "./graphql/api_schema.graphql", - query_path = "./graphql/queries/proofs_of_indexing.graphql", - response_derives = "Debug" -)] -struct ProofsOfIndexing; - -type POI = proofs_of_indexing::ProofsOfIndexingProofsOfIndexing; -type POISGroupedByBlock = BTreeMap>; - -async fn fetch_proofs_of_indexing(deployment: String) -> Result { - let query = ProofsOfIndexing::build_query(proofs_of_indexing::Variables { - request: proofs_of_indexing::ProofOfIndexingRequest { - deployments: vec![deployment.clone()], - blockRange: None, - limit: None, - }, - }); - let query = serde_json::json!(query); - - let request = Request::new("http://localhost:3030/graphql") - .body(query.to_string()) - .header("content-type", "application/json") - .method(Method::POST) - .mode(RequestMode::Cors); - - let response: GraphQLResponse = - request.send().await?.json().await?; - - let pois = match (response.data, response.errors) { - (Some(data), _) => data.proofs_of_indexing, - (_, Some(errors)) => { - warn!( - "Errors fetching proofs of indexing for deployment {}: {:?}", - deployment, errors - ); - vec![] - } - (_, _) => vec![], - }; - - // Group POIs by block number (using a BTreeMap to order the numbers) - let mut grouped_by_block = POISGroupedByBlock::new(); - - for poi in pois { - grouped_by_block - .entry(poi.block.number) - .or_insert_with(Vec::new) - .push(poi); - } - - // For each block number, sort POIs by indexer - for (_, pois) in grouped_by_block.iter_mut() { - pois.sort_by(|a, b| a.indexer.cmp(&b.indexer)); - } - - Ok(grouped_by_block) -} - -fn poll_proofs_of_indexing(deployment: String, link: Scope) { - spawn_local(async move { - let pois = match fetch_proofs_of_indexing(deployment).await { - Ok(pois) => pois, - Err(error) => { - warn!("Failed to fetch proofs of indexing: {}", error); - return; - } - }; - - let layout = Layout::from(pois); - - link.send_message(Msg::Update(layout)); - }); -} - -#[derive(Debug)] -pub enum Cell { - POI(POI, RGBA), - Placeholder, -} - -impl Cell { - fn render(&self) -> Html { - match self { - Cell::POI(poi, color) => html! { - - {&poi.proof_of_indexing[..7]} - - }, - Cell::Placeholder => html! { - {"-"} - }, - } - } -} - -#[derive(Debug)] -pub enum Row { - Block(i64, Vec), - Placeholder, -} - -impl Row { - fn render(&self) -> Html { - match self { - Row::Block(number, cells) => html! { - - {number} - { - for cells.iter().map(Cell::render) - } - - }, - Row::Placeholder => html! { - - {"..."} - - }, - } - } -} - -#[derive(Debug, Default)] -pub struct Layout { - pub indexers: Vec, - pub rows: Vec, -} - -impl Layout { - fn from(data: POISGroupedByBlock) -> Self { - let mut rows = vec![]; - - // Assign a column to each indexer - let mut indexer_columns = BTreeMap::new(); - let mut indexers = vec![]; - for group in data.values() { - for poi in group { - match indexer_columns.get(&poi.indexer) { - Some(_) => {} - None => { - indexer_columns.insert(poi.indexer.clone(), indexers.len()); - indexers.push(poi.indexer.clone()); - } - } - } - } - - let mut groups = data.into_iter().rev().peekable(); - while let Some((block_number, group)) = groups.next() { - // Create a new row for the block number - let mut cells = vec![]; - let mut color = rgba(17, 157, 164, 1.0).lighten(Ratio::from_percentage(40)); - let mut last_poi = None; - - // Add indexer POIs or placeholders - for poi in group { - // Identify the indexer column - let indexer_column = indexer_columns.get(&poi.indexer).unwrap(); - - // If the indexer is supposed to be at a later column, insert placeholders - // for every column (indexer) for which we don't have data - let current_column = cells.len(); - if *indexer_column > current_column { - for _ in current_column..*indexer_column { - cells.push(Cell::Placeholder); - } - } - - // Use a different color if the POI is different from the one in the previous column - if last_poi.is_none() || poi.proof_of_indexing.ne(last_poi.as_ref().unwrap()) { - last_poi = Some(poi.proof_of_indexing.clone()); - color = color.darken(Ratio::from_percentage(15)); - } - - cells.push(Cell::POI(poi, color)); - } - - // Add the row to the layout - rows.push(Row::Block(block_number, cells)); - - // If the next block number is not one block before, add a placeholder - if let Some((next_block_number, _)) = groups.peek() { - if next_block_number < &(block_number - 1) { - rows.push(Row::Placeholder); - } - } - } - - Self { indexers, rows } - } - - fn render(&self) -> Html { - html! { -
    - - - - - { - for self.indexers.iter().map(|i| html! { - - }) - } - <> - - - - - { - for self.rows.iter().map(Row::render) - } - -
    {"Block"}{i}
    -
    - } - } -} - -#[derive(Properties, PartialEq)] -pub struct ViewProps { - pub id: String, -} - -pub struct View { - layout: Layout, - _proofs_of_indexing_interval: Interval, -} - -pub enum Msg { - Update(Layout), -} - -impl Component for View { - type Message = Msg; - type Properties = ViewProps; - - fn create(ctx: &Context) -> Self { - let id = ctx.props().id.clone(); - let link = ctx.link().clone(); - - // Fetch deployments immediately - poll_proofs_of_indexing(id.clone(), link.clone()); - - Self { - layout: Layout::default(), - - // Refetch deployments every 5s - _proofs_of_indexing_interval: Interval::new(5000, move || { - poll_proofs_of_indexing(id.clone(), link.clone()) - }), - } - } - - fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { - match msg { - Msg::Update(layout) => { - self.layout = layout; - } - } - true - } - - fn view(&self, _ctx: &Context) -> Html { - html! { -
    - {self.layout.render()} -
    - } - } -} diff --git a/frontend/src/pages/poi_explorer/index.rs b/frontend/src/pages/poi_explorer/index.rs deleted file mode 100644 index edfde78..0000000 --- a/frontend/src/pages/poi_explorer/index.rs +++ /dev/null @@ -1,120 +0,0 @@ -use anyhow; -use gloo::timers::callback::Interval; -use graphql_client::{GraphQLQuery, Response as GraphQLResponse}; -use log::warn; -use reqwasm::http::*; -use stylist::yew::*; -use wasm_bindgen_futures::spawn_local; -use yew::{html::Scope, prelude::*}; -use yew_router::prelude::*; - -use crate::routes::*; - -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "./graphql/api_schema.graphql", - query_path = "./graphql/queries/deployments.graphql", - response_derives = "Debug" -)] -struct Deployments; - -async fn fetch_deployments() -> Result, anyhow::Error> { - let query = Deployments::build_query(deployments::Variables {}); - let query = serde_json::json!(query); - - let request = Request::new("http://localhost:3030/graphql") - .body(query.to_string()) - .header("content-type", "application/json") - .method(Method::POST) - .mode(RequestMode::Cors); - - let response: GraphQLResponse = request.send().await?.json().await?; - - match (response.data, response.errors) { - (Some(data), _) => Ok(data.deployments), - (_, Some(errors)) => { - warn!("Errors fetching deployments: {:?}", errors); - Ok(vec![]) - } - (_, _) => unreachable!(), - } -} - -fn poll_deployments(link: Scope) { - spawn_local(async move { - match fetch_deployments().await { - Ok(deployments) => link.send_message(Msg::UpdateDeployments(deployments)), - Err(error) => warn!("Failed to fetch deployments: {}", error), - } - }); -} - -#[derive(Properties, PartialEq)] -pub struct DeploymentLinkProps { - pub deployment: String, -} - -#[styled_component(DeploymentLink)] -pub fn deployment_link(props: &DeploymentLinkProps) -> Html { - html! { - to={Route::POIExplorerDeployment { id: props.deployment.clone() }}> - {&props.deployment} - > - } -} - -pub struct View { - deployments: Vec, - _deployments_interval: Interval, -} - -pub enum Msg { - UpdateDeployments(Vec), -} - -impl Component for View { - type Message = Msg; - type Properties = (); - - fn create(ctx: &Context) -> Self { - let link = ctx.link().clone(); - - // Fetch deployments immediately - poll_deployments(link.clone()); - - Self { - deployments: vec![], - - // Refetch deployments every 5s - _deployments_interval: Interval::new(5000, move || poll_deployments(link.clone())), - } - } - - fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { - match msg { - Msg::UpdateDeployments(deployments) => { - self.deployments = deployments; - } - } - true - } - - fn view(&self, _ctx: &Context) -> Html { - html! { - <> -

    {"Deployments"}

    -
      - { - for self.deployments.iter().map(|d| { - html!{ -
    • - -
    • - } - }) - } -
    - - } - } -} diff --git a/frontend/src/pages/poi_explorer/mod.rs b/frontend/src/pages/poi_explorer/mod.rs deleted file mode 100644 index b8c5817..0000000 --- a/frontend/src/pages/poi_explorer/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod index; -pub use index::View as Index; - -mod deployment; -pub use deployment::View as Deployment; diff --git a/frontend/src/pages/poi_reports/index.rs b/frontend/src/pages/poi_reports/index.rs deleted file mode 100644 index 994aa3a..0000000 --- a/frontend/src/pages/poi_reports/index.rs +++ /dev/null @@ -1,275 +0,0 @@ -use std::collections::{BTreeMap, BTreeSet}; - -use gloo::timers::callback::Interval; -use graphql_client::{GraphQLQuery, Response as GraphQLResponse}; -use log::warn; -use reqwasm::http::{Method, Request, RequestMode}; -use stylist::css; -use wasm_bindgen_futures::spawn_local; -use yew::{html::Scope, prelude::*}; -use yew_router::prelude::*; - -use crate::contexts::{Theme, ThemeContext}; -use crate::routes::Route; - -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "./graphql/api_schema.graphql", - query_path = "./graphql/queries/poi_cross_check_reports.graphql", - response_derives = "Debug, Clone" -)] -struct POICrossCheckReports; - -type POICrossCheckReport = poi_cross_check_reports::PoiCrossCheckReportsPoiCrossCheckReports; - -async fn fetch_cross_check_reports() -> Result, anyhow::Error> { - let query = POICrossCheckReports::build_query(poi_cross_check_reports::Variables { - request: poi_cross_check_reports::POICrossCheckReportRequest { - deployments: vec![], - indexer1: None, - indexer2: None, - }, - }); - let query = serde_json::json!(query); - - let request = Request::new("http://localhost:3030/graphql") - .body(query.to_string()) - .header("content-type", "application/json") - .method(Method::POST) - .mode(RequestMode::Cors); - - let response: GraphQLResponse = - request.send().await?.json().await?; - - let reports = match (response.data, response.errors) { - (Some(data), _) => data.poi_cross_check_reports, - (_, Some(errors)) => { - warn!("Errors fetching POI cross-check reports: {:?}", errors); - vec![] - } - (_, _) => vec![], - }; - - Ok(reports) -} - -fn poll_cross_check_reports(link: Scope) { - spawn_local(async move { - let reports = match fetch_cross_check_reports().await { - Ok(reports) => reports, - Err(error) => { - warn!("Failed to fetch POI cross-check reports: {}", error); - return; - } - }; - - let layout = Layout::from(reports); - - link.send_message(Msg::Update(layout)); - }); -} - -#[derive(Clone, Debug, Default)] -pub struct Cell { - pub reports: Vec, -} - -impl Cell { - fn render(&self, theme: &Theme) -> Html { - match self.reports.is_empty() { - true => html! { {"-"} }, - false => { - let conflicts = self - .reports - .iter() - .filter(|report| report.proof_of_indexing1.ne(&report.proof_of_indexing2)) - .collect::>(); - - match conflicts.is_empty() { - true => html! { - {"\u{00a0}"} - }, - false => { - let conflict = conflicts.iter().next().unwrap(); - - html! { - - to={Route::POIReportsForIndexers { indexer1: conflict.indexer1.clone(), indexer2: conflict.indexer2.clone() }}> - { - format!( - "{} {}", - conflicts.len(), - match conflicts.len() { - 1 => "conflict", - _ => "conflicts" - } - ) - } - > - - } - } - } - } - } - } -} - -#[derive(Clone, Debug)] -pub struct Row { - pub indexer: String, - pub cells: Vec, -} - -impl Row { - fn new(indexer: String, columns: usize) -> Self { - let mut cells = vec![]; - cells.resize(columns, Default::default()); - Self { indexer, cells } - } - - fn render(&self, theme: &Theme) -> Html { - html! { - - {&self.indexer} - { - for self.cells.iter().map(|cell| cell.render(theme)) - } - - } - } -} - -#[derive(Debug, Default)] -pub struct Layout { - pub indexer_indices: BTreeMap, - pub rows: Vec, -} - -impl From> for Layout { - fn from(reports: Vec) -> Self { - let indexer_indices = reports - .iter() - .map(|report| report.indexer1.clone()) - .chain(reports.iter().map(|report| report.indexer2.clone())) - .collect::>() - .into_iter() - .enumerate() - .map(|(i, indexer)| (indexer, i)) - .collect::>(); - - let mut rows = vec![]; - for indexer in indexer_indices.keys() { - rows.push(Row::new(indexer.clone(), indexer_indices.len())); - } - - for report in reports { - let indexer1_index = indexer_indices.get(&report.indexer1).unwrap(); - let indexer2_index = indexer_indices.get(&report.indexer2).unwrap(); - - rows[*indexer1_index].cells[*indexer2_index] - .reports - .push(report.clone()); - rows[*indexer2_index].cells[*indexer1_index] - .reports - .push(report); - } - - Self { - indexer_indices, - rows, - } - } -} - -impl Layout { - fn render(&self, theme: &Theme) -> Html { - html! { -
    - - - - { - for self.indexer_indices.keys().map(|indexer| { - html! { - - } - }) - } - - - { - for self.rows.iter().map(|row| row.render(theme)) - } - -
    {&indexer}
    -
    - } - } -} - -pub struct View { - layout: Layout, - _poll_interval: Interval, -} - -pub enum Msg { - Update(Layout), -} - -impl Component for View { - type Message = Msg; - type Properties = (); - - fn create(ctx: &Context) -> Self { - let link = ctx.link().clone(); - - poll_cross_check_reports(link.clone()); - - Self { - layout: Layout::default(), - - // Refetch reports every few seconds - _poll_interval: Interval::new(5000, move || poll_cross_check_reports(link.clone())), - } - } - - fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { - match msg { - Msg::Update(layout) => { - self.layout = layout; - } - } - true - } - - fn view(&self, ctx: &Context) -> Html { - let (theme, _) = ctx - .link() - .context::(Callback::noop()) - .expect("theme context to be set"); - - html! { -
    - {self.layout.render(&theme)} -
    - } - } -} diff --git a/frontend/src/pages/poi_reports/indexers.rs b/frontend/src/pages/poi_reports/indexers.rs deleted file mode 100644 index 5a75dd6..0000000 --- a/frontend/src/pages/poi_reports/indexers.rs +++ /dev/null @@ -1,300 +0,0 @@ -use std::collections::BTreeMap; - -use gloo::timers::callback::Interval; -use graphql_client::{GraphQLQuery, Response as GraphQLResponse}; -use log::warn; -use reqwasm::http::{Method, Request, RequestMode}; -use stylist::css; -use wasm_bindgen_futures::spawn_local; -use yew::{html::Scope, prelude::*}; -use yew_router::prelude::*; - -use crate::contexts::{Theme, ThemeContext}; -use crate::routes::Route; - -#[derive(GraphQLQuery)] -#[graphql( - schema_path = "./graphql/api_schema.graphql", - query_path = "./graphql/queries/poi_cross_check_reports.graphql", - response_derives = "Debug, Clone" -)] -struct POICrossCheckReports; - -type POICrossCheckReport = poi_cross_check_reports::PoiCrossCheckReportsPoiCrossCheckReports; - -async fn fetch_indexer_cross_check_reports( - indexer1: String, - indexer2: String, -) -> Result, anyhow::Error> { - let query = POICrossCheckReports::build_query(poi_cross_check_reports::Variables { - request: poi_cross_check_reports::POICrossCheckReportRequest { - deployments: vec![], - indexer1: Some(indexer1), - indexer2: Some(indexer2), - }, - }); - let query = serde_json::json!(query); - - let request = Request::new("http://localhost:3030/graphql") - .body(query.to_string()) - .header("content-type", "application/json") - .method(Method::POST) - .mode(RequestMode::Cors); - - let response: GraphQLResponse = - request.send().await?.json().await?; - - let reports = match (response.data, response.errors) { - (Some(data), _) => data.poi_cross_check_reports, - (_, Some(errors)) => { - warn!("Errors fetching POI cross-check reports: {:?}", errors); - vec![] - } - (_, _) => vec![], - }; - - Ok(reports) -} - -fn poll_indexer_cross_check_reports(indexer1: String, indexer2: String, link: Scope) { - spawn_local(async move { - let reports = match fetch_indexer_cross_check_reports(indexer1, indexer2).await { - Ok(reports) => reports, - Err(error) => { - warn!("Failed to fetch POI cross-check reports: {}", error); - return; - } - }; - - let layout = Layout::from(reports); - - link.send_message(Msg::Update(layout)); - }); -} - -#[derive(Clone, Debug, Default)] -pub struct Cell { - pub reports: Vec, -} - -impl Cell { - fn render(&self, theme: &Theme) -> Html { - match self.reports.is_empty() { - true => html! { {"-"} }, - false => { - let conflicts = self - .reports - .iter() - .filter(|report| report.proof_of_indexing1.ne(&report.proof_of_indexing2)) - .collect::>(); - - match conflicts.is_empty() { - true => html! { - {"\u{00a0}"} - }, - false => { - let conflict = conflicts.iter().next().unwrap(); - - html! { - - to={Route::POIReportsForIndexers { indexer1: conflict.indexer1.clone(), indexer2: conflict.indexer2.clone() }}> - { - format!( - "{} {}", - conflicts.len(), - match conflicts.len() { - 1 => "conflict", - _ => "conflicts" - } - ) - } - > - - } - } - } - } - } - } -} - -#[derive(Clone, Debug)] -pub struct Row { - pub indexer: String, - pub cells: Vec, -} - -impl Row { - fn new(indexer: String, columns: usize) -> Self { - let mut cells = vec![]; - cells.resize(columns, Default::default()); - Self { indexer, cells } - } - - fn render(&self, theme: &Theme) -> Html { - html! { - - {&self.indexer} - { - for self.cells.iter().map(|cell| cell.render(theme)) - } - - } - } -} - -#[derive(Debug, Default)] -pub struct Layout { - pub deployments: BTreeMap>, -} - -impl From> for Layout { - fn from(reports: Vec) -> Self { - let mut deployments = BTreeMap::>::new(); - - for report in reports { - deployments - .entry(report.deployment.clone()) - .or_default() - .push(report); - } - - Self { deployments } - } -} - -impl Layout { - fn render(&self, theme: &Theme) -> Html { - html! { -
    - { - match self.deployments.is_empty() { - true => html! {

    {"No reports generated yet."}

    }, - false => html! { - <> - { - for self.deployments.iter().map(|(deployment, reports)| { - html! { -
    -

    {"Deployment "}{&deployment}

    - - - - - - - - - - - <> - { - for reports.iter().map(|report| { - html! { - - - - - - - } - }) - } - - -
    {"Block"}{"POI 1"}{"POI 2"}{"Diverging Block"}
    {report.block.number}{" ("}{report.block.hash.clone().unwrap_or(String::from("-"))}{")"}{&report.proof_of_indexing1[0..7]}{&report.proof_of_indexing2[0..7]} - //
    {report.diverging_block.clone().map_or(String::from("-"), |diverging_block| format!("{:#?}", diverging_block))}
    -
    -
    - } - }) - } - - } - } - } -
    - } - } -} - -pub struct View { - layout: Layout, - _poll_interval: Interval, -} - -pub enum Msg { - Update(Layout), -} - -#[derive(PartialEq, Properties)] -pub struct ViewProperties { - pub indexer1: String, - pub indexer2: String, -} - -impl Component for View { - type Message = Msg; - type Properties = ViewProperties; - - fn create(ctx: &Context) -> Self { - let indexer1 = ctx.props().indexer1.clone(); - let indexer2 = ctx.props().indexer2.clone(); - - let link = ctx.link().clone(); - - poll_indexer_cross_check_reports(indexer1.clone(), indexer2.clone(), link.clone()); - - Self { - layout: Layout::default(), - - // Refetch reports every few seconds - _poll_interval: Interval::new(5000, move || { - poll_indexer_cross_check_reports(indexer1.clone(), indexer2.clone(), link.clone()) - }), - } - } - - fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { - match msg { - Msg::Update(layout) => { - self.layout = layout; - } - } - true - } - - fn view(&self, ctx: &Context) -> Html { - let (theme, _) = ctx - .link() - .context::(Callback::noop()) - .expect("theme context to be set"); - - html! { -
    - {self.layout.render(&theme)} -
    - } - } -} diff --git a/frontend/src/pages/poi_reports/mod.rs b/frontend/src/pages/poi_reports/mod.rs deleted file mode 100644 index 4bab4f2..0000000 --- a/frontend/src/pages/poi_reports/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod index; -pub use index::View as Index; - -mod indexers; -pub use indexers::View as Indexers; diff --git a/frontend/src/routes.rs b/frontend/src/routes.rs deleted file mode 100644 index 7f3ad8c..0000000 --- a/frontend/src/routes.rs +++ /dev/null @@ -1,58 +0,0 @@ -use yew::prelude::*; -use yew_router::prelude::*; - -use crate::components::*; -use crate::pages::*; - -#[derive(Clone, Routable, PartialEq)] -pub enum Route { - #[at("/")] - Overview, - #[at("/poi-explorer")] - POIExplorer, - #[at("/poi-reports/deployment/:id")] - POIExplorerDeployment { id: String }, - #[at("/poi-reports")] - POIReports, - #[at("/poi-reports/indexers/:indexer1/:indexer2")] - POIReportsForIndexers { indexer1: String, indexer2: String }, - #[not_found] - #[at("/404")] - NotFound, -} - -pub fn switch(routes: &Route) -> Html { - match routes.clone() { - Route::Overview | Route::POIExplorer => { - html! { - - - - } - } - Route::POIExplorerDeployment { id } => { - html! { - - - - } - } - Route::POIReports => { - html! { - - - - } - } - Route::POIReportsForIndexers { indexer1, indexer2 } => { - html! { - - - - } - } - Route::NotFound => { - html! {
    {"404 Not Found"}
    } - } - } -} diff --git a/ops/compose/grafana/dashboards/Graphix_Dashboard.json b/ops/compose/grafana/dashboards/Graphix_Dashboard.json index 8e0f168..b4a38a8 100644 --- a/ops/compose/grafana/dashboards/Graphix_Dashboard.json +++ b/ops/compose/grafana/dashboards/Graphix_Dashboard.json @@ -61,7 +61,72 @@ ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "displayName", + "value": "Deployment ID" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "networkName" + }, + "properties": [ + { + "id": "displayName", + "value": "Network" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "name" + }, + "properties": [ + { + "id": "displayName", + "value": "Sg. name" + }, + { + "id": "noValue", + "value": "-" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Deployment ID" + }, + "properties": [ + { + "id": "custom.width", + "value": 470 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Network" + }, + "properties": [ + { + "id": "custom.width", + "value": 98 + } + ] + } + ] }, "gridPos": { "h": 16, @@ -74,15 +139,17 @@ "cellHeight": "sm", "footer": { "countRows": false, + "enablePagination": true, "fields": "", "reducer": [ "sum" ], "show": false }, - "showHeader": true + "showHeader": true, + "sortBy": [] }, - "pluginVersion": "9.5.1", + "pluginVersion": "9.4.7", "targets": [ { "aliasBy": "", @@ -102,7 +169,7 @@ "timePath": "" } ], - "title": "Deployments", + "title": "Monitored deployments", "type": "table" }, { @@ -293,6 +360,101 @@ "transformations": [], "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "c19gyBP4z" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "hertz" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 15, + "x": 9, + "y": 17 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "c19gyBP4z" + }, + "editorMode": "code", + "expr": "sum (rate(public_proofs_of_indexing_requests[$__rate_interval]))", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "RPS: \"publicProofsOfIndexing\"", + "transformations": [], + "type": "timeseries" + }, { "datasource": { "type": "fifemon-graphql-datasource", @@ -321,10 +483,10 @@ "overrides": [] }, "gridPos": { - "h": 7, - "w": 9, - "x": 0, - "y": 16 + "h": 4, + "w": 5, + "x": 9, + "y": 25 }, "id": 3, "options": { @@ -341,7 +503,7 @@ }, "textMode": "auto" }, - "pluginVersion": "9.5.1", + "pluginVersion": "9.4.7", "targets": [ { "aliasBy": "", @@ -380,43 +542,175 @@ }, { "datasource": { - "type": "prometheus", - "uid": "c19gyBP4z" + "type": "fifemon-graphql-datasource", + "uid": "gYhzyBE4k" }, + "description": "List of indexers monitored by Graphix", "fieldConfig": { "defaults": { "color": { - "mode": "palette-classic" + "mode": "thresholds" }, "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false + "align": "auto", + "cellOptions": { + "type": "auto" }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "address" }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" + "properties": [ + { + "id": "displayName", + "value": "Indexer ID (address)" + }, + { + "id": "custom.filterable", + "value": true + }, + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Indexer page on the Network Explorer", + "url": "https://thegraph.com/explorer/profile/${__value.text}?view=Overview&chain=mainnet" + }, + { + "targetBlank": true, + "title": "Etherscan", + "url": "https://etherscan.io/address/${__value.text}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "allocatedTokens" }, - "thresholdsStyle": { - "mode": "off" - } + "properties": [ + { + "id": "noValue", + "value": "-" + }, + { + "id": "displayName", + "value": "Total allocated tokens" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "name" + }, + "properties": [ + { + "id": "displayName", + "value": "Default display name" + }, + { + "id": "noValue", + "value": "-" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Indexer ID (address)" + }, + "properties": [ + { + "id": "custom.width", + "value": 425 + } + ] + } + ] + }, + "gridPos": { + "h": 14, + "w": 10, + "x": 14, + "y": 25 + }, + "id": 9, + "options": { + "footer": { + "countRows": false, + "enablePagination": true, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "aliasBy": "", + "annotationTags": "", + "annotationText": "", + "annotationTitle": "", + "constant": 6.5, + "dataPath": "indexers", + "datasource": { + "type": "fifemon-graphql-datasource", + "uid": "gYhzyBE4k" + }, + "endTimePath": "endTime", + "groupBy": "", + "queryText": "query {\n indexers(filter: {}) {\n address\n name\n allocatedTokens\n }\n}", + "refId": "A", + "timePath": "" + } + ], + "title": "Monitored indexers", + "type": "table" + }, + { + "datasource": { + "type": "fifemon-graphql-datasource", + "uid": "gYhzyBE4k" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false }, "mappings": [], "thresholds": { @@ -431,47 +725,89 @@ "value": 80 } ] - }, - "unit": "hertz" + } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "name" + }, + "properties": [ + { + "id": "displayName", + "value": "Name" + }, + { + "id": "custom.filterable", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "caip2" + }, + "properties": [ + { + "id": "displayName", + "value": "CAIP-2 ID" + }, + { + "id": "noValue", + "value": "-" + } + ] + } + ] }, "gridPos": { - "h": 8, - "w": 15, + "h": 10, + "w": 5, "x": 9, - "y": 17 + "y": 29 }, - "id": 7, + "id": 11, "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "CAIP2 network ID (if known)" + } + ] }, + "pluginVersion": "9.4.7", "targets": [ { + "aliasBy": "", + "annotationTags": "", + "annotationText": "", + "annotationTitle": "", + "constant": 6.5, + "dataPath": "networks", "datasource": { - "type": "prometheus", - "uid": "c19gyBP4z" + "type": "fifemon-graphql-datasource", + "uid": "gYhzyBE4k" }, - "editorMode": "code", - "expr": "sum (rate(public_proofs_of_indexing_requests[$__rate_interval]))", - "hide": false, - "legendFormat": "__auto", - "range": true, - "refId": "A" + "endTimePath": "endTime", + "groupBy": "", + "queryText": "query {\n networks {\n name\n caip2\n }\n}", + "refId": "A", + "timePath": "" } ], - "title": "RPS: \"publicProofsOfIndexing\"", - "transformations": [], - "type": "timeseries" + "title": "Networks with known subgraphs", + "type": "table" } ], "refresh": "", @@ -490,6 +826,6 @@ "timezone": "", "title": "Graphix Dashboard", "uid": "dee4d09d-0dbc-4af2-b4a2-2fcb9ecf35cc", - "version": 5, + "version": 9, "weekStart": "" } diff --git a/ops/compose/grafana/data/grafana.db b/ops/compose/grafana/data/grafana.db index f656a10..8391fc3 100644 Binary files a/ops/compose/grafana/data/grafana.db and b/ops/compose/grafana/data/grafana.db differ diff --git a/ops/compose/graphix/network.yml b/ops/compose/graphix/network.yml index e52e223..fcb445c 100644 --- a/ops/compose/graphix/network.yml +++ b/ops/compose/graphix/network.yml @@ -1,3 +1,5 @@ +apiPort: 3030 + # A good mix of data sources for local development, both mainnet and testnet. databaseUrl: postgres://graphix:password@postgres-graphix:5432/graphix @@ -17,8 +19,8 @@ sources: # query: byAllocations # stakeThreshold: 0.0 # limit: 1000 - - type: networkSubgraph - endpoint: https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum - query: byStakedTokens - stakeThreshold: 0.0 - limit: 1000 + #- type: networkSubgraph + # endpoint: https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-arbitrum + # query: byStakedTokens + # stakeThreshold: 0.0 + # limit: 1000 diff --git a/ops/compose/graphix/two-empty-graph-nodes.yml b/ops/compose/graphix/two-empty-graph-nodes.yml index 6e4a132..9b09396 100644 --- a/ops/compose/graphix/two-empty-graph-nodes.yml +++ b/ops/compose/graphix/two-empty-graph-nodes.yml @@ -1,3 +1,5 @@ +apiPort: 3030 + databaseUrl: postgres://graphix:password@postgres-graphix:5432/graphix environments: - id: graph-node-1 diff --git a/ops/compose/network.yml b/ops/compose/network.yml index 8dafc4b..8ce5cca 100644 --- a/ops/compose/network.yml +++ b/ops/compose/network.yml @@ -1,7 +1,7 @@ version: "3" services: grafana: - image: grafana/grafana-enterprise + image: grafana/grafana-oss restart: always # https://community.grafana.com/t/new-docker-install-with-persistent-storage-permission-problem/10896/16 user: ":" @@ -30,15 +30,16 @@ services: - --web.console.templates=/usr/share/prometheus/consoles ports: - "9090:9090" + logging: + driver: none volumes: - ./prometheus/:/etc/prometheus/ - graphix-cross-checker: - image: edgeandnode/graphix-cross-checker + graphix: + image: edgeandnode/graphix restart: on-failure build: context: ../.. - dockerfile: ops/cross-checker.dockerfile args: CARGO_PROFILE: dev depends_on: @@ -48,34 +49,10 @@ services: RUST_LOG: graphix=debug ports: - "9184:9184" - volumes: - - ./graphix/:/config/ - command: ["--config", "/config/network.yml"] - - graphix-api-server: - image: edgeandnode/graphix-api-server - restart: on-failure - build: - context: ../.. - dockerfile: ops/api-server.dockerfile - args: - CARGO_PROFILE: dev - ports: - "3030:3030" - depends_on: - postgres-graphix: - condition: service_healthy - environment: - RUST_LOG: graphix=debug volumes: - ./graphix/:/config/ - command: - [ - "--database-url", - "postgres://graphix:password@postgres-graphix:5432/graphix", - "--port", - "3030", - ] + command: ["--config", "/config/two-empty-graph-nodes.yml"] postgres-graphix: image: postgres diff --git a/ops/compose/two-empty-graph-nodes.yml b/ops/compose/two-empty-graph-nodes.yml index b24f939..953bd7f 100644 --- a/ops/compose/two-empty-graph-nodes.yml +++ b/ops/compose/two-empty-graph-nodes.yml @@ -44,12 +44,11 @@ services: timeout: 1s retries: 1000 - graphix-cross-checker: - image: edgeandnode/graphix-cross-checker + graphix: + image: edgeandnode/graphix restart: on-failure build: context: ../.. - dockerfile: ops/cross-checker.dockerfile args: CARGO_PROFILE: dev depends_on: @@ -59,33 +58,10 @@ services: RUST_LOG: graphix=debug ports: - "9184:9184" - volumes: - - ./graphix/:/config/ - command: ["--config", "/config/two-empty-graph-nodes.yml"] - graphix-api-server: - image: edgeandnode/graphix-api-server - restart: on-failure - build: - context: ../.. - dockerfile: ops/api-server.dockerfile - args: - CARGO_PROFILE: dev - ports: - "3030:3030" - depends_on: - postgres-graphix: - condition: service_healthy - environment: - RUST_LOG: graphix=debug volumes: - ./graphix/:/config/ - command: - [ - "--database-url", - "postgres://graphix:password@postgres-graphix:5432/graphix", - "--port", - "3030", - ] + command: ["--config", "/config/two-empty-graph-nodes.yml"] graph-node-1: image: graphprotocol/graph-node diff --git a/ops/cross-checker.dockerfile b/ops/cross-checker.dockerfile deleted file mode 100644 index 756dc42..0000000 --- a/ops/cross-checker.dockerfile +++ /dev/null @@ -1,51 +0,0 @@ -FROM rust:slim-bullseye AS chef - -WORKDIR /app -COPY rust-toolchain.toml . -# Triggers the install of the predefined Rust toolchain. -# See . -RUN rustup show -RUN cargo install cargo-chef - -FROM chef AS planner - -COPY . . -RUN cargo chef prepare --recipe-path recipe.json - -FROM chef AS builder - -# We support both `release` and `dev`. -ARG CARGO_PROFILE=release - -COPY --from=planner /app/recipe.json recipe.json - -RUN apt-get update && apt-get install -y libpq-dev ca-certificates pkg-config libssl-dev - -# Use cargo-chef to compile dependencies only - this will be cached by Docker. -RUN cargo chef cook --profile $CARGO_PROFILE --recipe-path recipe.json --bin graphix-cross-checker -# ... and then build the rest of the application. -COPY . . -RUN cargo build --profile $CARGO_PROFILE --bin graphix-cross-checker - -# Instead of calculating where the binary is located based on $CARGO_PROFILE, we -# simply try to copy both `debug` and `release` binaries. -RUN cp target/release/graphix-cross-checker /usr/local/bin | true && \ - cp target/debug/graphix-cross-checker /usr/local/bin | true - -FROM debian:buster-slim - -WORKDIR /app - -RUN apt-get update && \ - apt-get install -y libpq-dev ca-certificates libssl-dev && \ - apt-get clean - -COPY --from=builder /usr/local/bin/graphix-cross-checker /usr/local/bin -COPY --from=builder /app/examples/testnet.yml /app/config.yml - -ENV RUST_LOG="graphix=debug" - -EXPOSE 14265 - -ENTRYPOINT [ "graphix-cross-checker" ] -CMD ["--config", "/app/config.yml"] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 469a750..de3b853 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.70.0" +channel = "1.75.0" targets = ["wasm32-unknown-unknown"] profile = "default" diff --git a/schema.rs b/schema.rs new file mode 100644 index 0000000..363ebf0 --- /dev/null +++ b/schema.rs @@ -0,0 +1 @@ +The --database-url argument must be passed, or the DATABASE_URL environment variable must be set.