diff --git a/Cargo.lock b/Cargo.lock index cf3ebbcf0..3e64eceff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" dependencies = [ "backtrace", ] @@ -218,7 +218,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.34", + "time 0.3.36", ] [[package]] @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", "event-listener 5.3.0", @@ -297,11 +297,10 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f98c37cf288e302c16ef6c8472aad1e034c6c84ce5ea7b8101c98eb4a802fee" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", "fastrand 2.0.2", @@ -327,7 +326,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.2.1", "async-executor", "async-io 2.3.2", "async-lock 3.3.0", @@ -368,8 +367,8 @@ dependencies = [ "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.6.0", - "rustix 0.38.32", + "polling 3.7.0", + "rustix 0.38.34", "slab", "tracing", "windows-sys 0.52.0", @@ -419,26 +418,26 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.32", + "rustix 0.38.34", "windows-sys 0.48.0", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" dependencies = [ "async-io 2.3.2", - "async-lock 2.8.0", + "async-lock 3.3.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.32", + "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -487,7 +486,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -498,13 +497,13 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -874,7 +873,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.2.1", "async-lock 3.3.0", "async-task", "fastrand 2.0.2", @@ -964,7 +963,7 @@ dependencies = [ "serde_with 2.3.3", "siwe", "thiserror", - "time 0.3.34", + "time 0.3.36", "url", ] @@ -1038,12 +1037,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.92" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -1311,6 +1311,7 @@ dependencies = [ "iroh-rpc-types", "libp2p", "libp2p-identity", + "loom", "lru 0.10.1", "prometheus-client", "rand 0.8.5", @@ -1392,9 +1393,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1402,7 +1403,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1529,7 +1530,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1894,7 +1895,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1982,7 +1983,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2015,7 +2016,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2217,12 +2218,6 @@ dependencies = [ "static-iref", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "digest" version = "0.8.1" @@ -2303,14 +2298,14 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "dissimilar" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" +checksum = "440d59c0c6d96354061909b4769b2ca03868dbaee203e7b779d9021ebbde3058" [[package]] name = "dotenvy" @@ -2406,9 +2401,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" dependencies = [ "serde", ] @@ -2463,9 +2458,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -2479,7 +2474,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2644,9 +2639,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" +checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" [[package]] name = "finl_unicode" @@ -2679,15 +2674,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - [[package]] name = "flume" version = "0.10.14" @@ -2860,7 +2846,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2920,6 +2906,20 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186014d53bc231d0090ef8d6f03e0920c54d85a5ed22f4f2f74315ec56cf83fb" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.54.0", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -2997,7 +2997,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -3206,9 +3206,9 @@ checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" [[package]] name = "hickory-proto" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091a6fbccf4860009355e3efc52ff4acf37a63489aad7435372d44ceeb6fbbcf" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" dependencies = [ "async-trait", "cfg-if", @@ -3231,9 +3231,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b8f021164e6a984c9030023544c57789c51760065cd510572fedcfb04164e8" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" dependencies = [ "cfg-if", "futures-util", @@ -3511,7 +3511,7 @@ dependencies = [ "smol", "system-configuration", "tokio", - "windows", + "windows 0.51.1", ] [[package]] @@ -3819,9 +3819,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] @@ -4842,7 +4842,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5053,6 +5053,19 @@ dependencies = [ "value-bag", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lru" version = "0.10.1" @@ -5190,9 +5203,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.11.4" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" dependencies = [ "cfg-if", "downcast", @@ -5205,14 +5218,14 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.11.4" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -5494,12 +5507,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - [[package]] name = "ntapi" version = "0.4.1" @@ -5725,7 +5732,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6028,7 +6035,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6177,15 +6184,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi 0.3.9", "pin-project-lite", - "rustix 0.38.32", + "rustix 0.38.34", "tracing", "windows-sys 0.52.0", ] @@ -6233,16 +6240,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "2.1.5" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ - "difflib", - "float-cmp", - "itertools 0.10.5", - "normalize-line-endings", + "anstyle", "predicates-core", - "regex", ] [[package]] @@ -6346,9 +6349,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -6379,7 +6382,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6555,9 +6558,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -6667,7 +6670,7 @@ checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6" dependencies = [ "pem", "ring 0.16.20", - "time 0.3.34", + "time 0.3.36", "yasna", ] @@ -6702,6 +6705,8 @@ dependencies = [ "libp2p", "libp2p-identity", "libp2p-swarm-test", + "loom", + "mockall", "multihash 0.18.1", "pin-project", "pretty", @@ -7022,9 +7027,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -7035,9 +7040,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", "ring 0.17.8", @@ -7123,6 +7128,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[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.2.0" @@ -7201,9 +7212,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] @@ -7247,13 +7258,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -7290,9 +7301,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -7344,7 +7355,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros 2.3.3", - "time 0.3.34", + "time 0.3.36", ] [[package]] @@ -7368,7 +7379,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -7462,9 +7473,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -7532,7 +7543,7 @@ dependencies = [ "rand 0.8.5", "sha3", "thiserror", - "time 0.3.34", + "time 0.3.36", ] [[package]] @@ -8349,9 +8360,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -8434,7 +8445,7 @@ checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand 2.0.2", - "rustix 0.38.32", + "rustix 0.38.34", "windows-sys 0.52.0", ] @@ -8482,7 +8493,7 @@ checksum = "c8f546451eaa38373f549093fe9fd05e7d2bade739e2ddf834b9968621d60107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8516,22 +8527,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8588,9 +8599,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -8609,9 +8620,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -8698,7 +8709,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8943,7 +8954,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -9374,7 +9385,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -9408,7 +9419,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9444,7 +9455,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.32", + "rustix 0.38.34", ] [[package]] @@ -9482,11 +9493,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -9505,6 +9516,16 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-targets 0.52.5", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -9520,7 +9541,26 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -9538,7 +9578,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -9558,17 +9598,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -9579,9 +9620,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -9591,9 +9632,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -9603,9 +9644,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -9615,9 +9662,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -9627,9 +9674,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -9639,9 +9686,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -9651,9 +9698,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -9706,7 +9753,7 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.34", + "time 0.3.36", ] [[package]] @@ -9761,7 +9808,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.34", + "time 0.3.36", ] [[package]] @@ -9781,7 +9828,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -9801,5 +9848,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] diff --git a/Cargo.toml b/Cargo.toml index 72b8e71c5..20a710895 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ ceramic-store = { path = "./store" } cid = { version = "0.10", features = ["serde-codec"] } clap = { version = "4", features = ["derive", "env"] } clap_mangen = "0.2.2" +concurrent-queue = "2.4.0" console = { version = "0.15", default-features = false } console-subscriber = "0.1.7" criterion = "0.4" @@ -69,6 +70,7 @@ deadqueue = "0.2.3" derivative = "2.2" derive_more = "0.99.17" dirs-next = "2" +event-listener = "4.0.3" expect-test = "1.4.1" fastmurmur3 = "0.1.2" fnv = "1.0.7" @@ -100,12 +102,14 @@ libipld = "0.16" libipld-cbor = "0.16" libp2p = { version = "0.53", default-features = false } libp2p-identity = { version = "0.2", features = ["peerid", "ed25519"] } +libp2p-swarm-test = { version = "0.3.0" } +loom = "0.7.1" lru = "0.10" mime = "0.3" mime_classifier = "0.0.1" mime_guess = "2.0.4" minicbor = { version = "0.19.1", features = ["std", "derive", "half"] } -mockall = "0.11.4" +mockall = "0.12.1" multiaddr = "0.18" multibase = "0.9" multihash = { version = "0.18", features = ["identity"] } @@ -126,7 +130,7 @@ quic-rpc = { version = "0.3.2", default-features = false } rand = "0.8.5" rand_chacha = "0.3.1" rayon = "1.5.3" -recon = { path = "./recon/" } +recon = { path = "./recon" } regex = "1.7.1" relative-path = "1.7.2" reqwest = { version = "0.11.10", default-features = false } diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index 2349a95b4..7a2a6edb1 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -69,6 +69,10 @@ criterion.workspace = true rand_chacha.workspace = true test-log.workspace = true ceramic-store.workspace = true +recon = { workspace = true, features = ["test-utils"]} + +#[target.'cfg(loomtest)'.dependencies] +loom.workspace = true [[bench]] name = "lru_cache" diff --git a/p2p/src/node.rs b/p2p/src/node.rs index 20166544f..c792b6647 100644 --- a/p2p/src/node.rs +++ b/p2p/src/node.rs @@ -62,10 +62,10 @@ pub enum NetworkEvent { /// /// Node provides an external API via RpcMessages. pub struct Node -where - I: Recon, - M: Recon, - S: iroh_bitswap::Store, + where + I: Recon, + M: Recon, + S: iroh_bitswap::Store, { metrics: Metrics, swarm: Swarm>, @@ -89,10 +89,10 @@ where } impl fmt::Debug for Node -where - I: Recon, - M: Recon, - S: iroh_bitswap::Store, + where + I: Recon, + M: Recon, + S: iroh_bitswap::Store, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Node") @@ -122,10 +122,10 @@ const BOOTSTRAP_INTERVAL: Duration = Duration::from_secs(5 * 60); const EXPIRY_INTERVAL: Duration = Duration::from_secs(1); impl Drop for Node -where - I: Recon, - M: Recon, - S: iroh_bitswap::Store, + where + I: Recon, + M: Recon, + S: iroh_bitswap::Store, { fn drop(&mut self) { self.rpc_task.abort(); @@ -136,10 +136,10 @@ where // We are not using IntoConnectionHandler directly only referencing the type as part of this event signature. type NodeSwarmEvent = SwarmEvent< as NetworkBehaviour>::ToSwarm>; impl Node -where - I: Recon + Send + Sync, - M: Recon + Send + Sync, - S: iroh_bitswap::Store + Send + Sync, + where + I: Recon + Send + Sync, + M: Recon + Send + Sync, + S: iroh_bitswap::Store + Send + Sync, { pub async fn new( config: Config, @@ -164,7 +164,7 @@ where block_store, metrics.clone(), ) - .await?; + .await?; info!("iroh-p2p peerid: {}", swarm.local_peer_id()); for addr in &libp2p_config.external_multiaddrs { @@ -214,8 +214,8 @@ where "/meshsub/1.0.0", "/meshsub/1.1.0", ] - .iter() - .map(|p| p.to_string()), + .iter() + .map(|p| p.to_string()), ), net_receiver_in: network_receiver_in, dial_queries: Default::default(), @@ -558,7 +558,7 @@ where response_channel: response, limit, }) - .await?; + .await?; Ok(None) } BitswapEvent::Ping { peer, response } => { @@ -619,9 +619,9 @@ where Ok(None) } QueryResult::Bootstrap(Ok(BootstrapOk { - peer, - num_remaining, - })) => { + peer, + num_remaining, + })) => { debug!( "kad bootstrap done {:?}, remaining: {}", peer, num_remaining @@ -658,9 +658,9 @@ where Ok(None) } QueryResult::GetClosestPeers(Err(GetClosestPeersError::Timeout { - key, - .. - })) => { + key, + .. + })) => { debug!("GetClosestPeers Timeout: {:?}", key); if let Some((peer_id, channels)) = self.find_on_dht_queries.remove(&key) { @@ -670,7 +670,7 @@ where "Failed to find peer {:?} on the DHT: Timeout", peer_id ))) - .ok(); + .ok(); } }); } @@ -791,7 +791,7 @@ where peer_id, error ))) - .ok(); + .ok(); } } Ok(None) @@ -840,9 +840,9 @@ where Ok(None) } Event::Autonat(autonat::Event::OutboundProbe(OutboundProbeEvent::Response { - address, - .. - })) => { + address, + .. + })) => { if !self.swarm.external_addresses().any(|addr| addr == &address) { debug!( %address, @@ -969,7 +969,7 @@ where } RpcMessage::NetPeers(response_channel) => { #[allow(clippy::needless_collect)] - let peers = self.swarm.connected_peers().copied().collect::>(); + let peers = self.swarm.connected_peers().copied().collect::>(); let peer_addresses: HashMap> = peers .into_iter() .map(|pid| { @@ -1190,8 +1190,8 @@ mod tests { server_addr, client_addr, ) - .await - .unwrap(); + .await + .unwrap(); Ok(()) } @@ -1210,7 +1210,7 @@ mod tests { server_addr, client_addr, ) - .await?; + .await?; Ok(()) } @@ -1239,8 +1239,8 @@ mod tests { #[async_trait] impl Recon for DummyRecon - where - K: recon::Key + where + K: recon::Key + std::fmt::Debug + serde::Serialize + for<'de> serde::Deserialize<'de> @@ -1375,7 +1375,7 @@ mod tests { 116, 51, 223, 7, 47, 24, 92, 233, 253, 5, 82, 72, 156, 214, 211, 143, 182, 206, 76, 207, 121, 235, 48, 31, 50, 60, 219, 157, ]) - .unwrap() + .unwrap() }; let keypair = Keypair::Ed25519(keypair); let libp2p_keypair: Libp2pKeypair = keypair.clone().into(); @@ -1393,7 +1393,7 @@ mod tests { Arc::new(SqliteEventStore::new(sql_pool).await?), metrics, ) - .await?; + .await?; let cfg = iroh_rpc_client::Config { p2p_addr: Some(rpc_client_addr), channels: Some(1), @@ -1725,8 +1725,8 @@ mod tests { Duration::from_secs(6), poll_for_providers(test_runner_a.client.clone(), &cid), ) - .await - .context("timed out before finding providers for the given cid")??; + .await + .context("timed out before finding providers for the given cid")??; assert!(providers.len() == 1); assert!(providers.first().unwrap().contains(&test_runner_c.peer_id)); @@ -1758,4 +1758,26 @@ mod tests { return Ok(providers); } } -} + + // #[cfg(loomtest)] + #[tokio::test] + async fn basic_loom_test() { + loom::model(|| { + let rt = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + rt.block_on(async { + let test_runner = TestRunnerBuilder::new() + .no_bootstrap() + .build() + .await + .unwrap(); + let got_peer_id = test_runner.client.local_peer_id().await.unwrap(); + let expect_peer_id: PeerId = "12D3KooWFma2D63TG9ToSiRsjFkoNm2tTihScTBAEdXxinYk5rwE" + .parse() + .unwrap(); + assert_eq!(expect_peer_id, got_peer_id); + }) + }); + } +} \ No newline at end of file diff --git a/recon/Cargo.toml b/recon/Cargo.toml index e28b41c54..a674a4904 100644 --- a/recon/Cargo.toml +++ b/recon/Cargo.toml @@ -18,6 +18,8 @@ futures.workspace = true hex = "0.4.3" libp2p-identity.workspace = true libp2p.workspace = true +libp2p-swarm-test = { workspace = true, optional = true } +mockall = { workspace = true, optional = true } multihash.workspace = true prometheus-client.workspace = true serde.workspace = true @@ -32,7 +34,8 @@ codespan-reporting = "0.11.1" expect-test.workspace = true lalrpop-util = { version = "0.20.0", features = ["lexer"] } libp2p = { workspace = true, features = ["ping"] } -libp2p-swarm-test = "0.3.0" +libp2p-swarm-test = { workspace = true } +mockall.workspace = true pin-project = "1.1.3" pretty = "0.12.1" quickcheck = "1.0.3" @@ -44,5 +47,12 @@ tokio-stream.workspace = true tokio-util.workspace = true tracing-subscriber.workspace = true +#[target.'cfg(loomtest)'.dependencies] +loom.workspace = true + [build-dependencies] lalrpop = "0.20.0" + +[features] +default = [] +test-utils = ["mockall", "libp2p-swarm-test"] diff --git a/recon/src/lib.rs b/recon/src/lib.rs index d3fb3cdfe..09af1334d 100644 --- a/recon/src/lib.rs +++ b/recon/src/lib.rs @@ -1,16 +1,16 @@ //! Recon is a network protocol for set reconciliation -#![warn(missing_docs, missing_debug_implementations, clippy::all)] +#![warn(missing_docs, clippy::all)] pub use crate::{ client::{Client, Server}, metrics::Metrics, recon::{ - btreestore::BTreeStore, AssociativeHash, EventIdStore, FullInterests, HashCount, - InsertResult, InterestProvider, InterestStore, Key, Range, Recon, ReconInterestProvider, - ReconItem, Store, SyncState, + AssociativeHash, EventIdStore, FullInterests, HashCount, InsertResult, InterestProvider, + InterestStore, Key, Range, Recon, ReconInterestProvider, ReconItem, Store, SyncState, }, sha256a::Sha256a, }; +pub use test_utils::BTreeStore; mod client; pub mod libp2p; @@ -19,5 +19,5 @@ pub mod protocol; mod recon; mod sha256a; -#[cfg(test)] -mod tests; +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; diff --git a/recon/src/libp2p.rs b/recon/src/libp2p.rs index 2e6cb57d0..120715368 100644 --- a/recon/src/libp2p.rs +++ b/recon/src/libp2p.rs @@ -39,6 +39,9 @@ use crate::{ Sha256a, }; +/// Events sent to swarm +pub type ToSwarmEvent = ToSwarm; + /// Name of the Recon protocol for synchronizing interests pub const PROTOCOL_NAME_INTEREST: &str = "/ceramic/recon/0.1.0/interest"; /// Name of the Recon protocol for synchronizing models @@ -130,12 +133,15 @@ impl Behaviour { } } - fn send_event(&self, event: ToSwarm) { + fn send_event(&self, event: ToSwarmEvent) { let tx = self.swarm_events_sender.clone(); + #[cfg(not(loomtest))] let _ = tokio::task::block_in_place(move || { let handle = tokio::runtime::Handle::current(); handle.block_on(async move { tx.send(event).await }) }); + #[cfg(loomtest)] + let _ = tx.blocking_send(event); } } diff --git a/recon/src/libp2p/tests.rs b/recon/src/libp2p/tests.rs index ebe8535f0..1dd527a57 100644 --- a/recon/src/libp2p/tests.rs +++ b/recon/src/libp2p/tests.rs @@ -1,242 +1,37 @@ -use std::sync::{atomic::AtomicBool, Arc}; - -use anyhow::{bail, Result}; -use async_trait::async_trait; -use ceramic_core::RangeOpen; -use ceramic_metrics::init_local_tracing; -use libp2p::{metrics::Registry, PeerId, Swarm}; -use libp2p_swarm_test::SwarmExt; +use libp2p::PeerId; use tracing::info; -use crate::{ - libp2p::{stream_set::StreamSet, PeerEvent, PeerStatus}, - AssociativeHash, BTreeStore, FullInterests, HashCount, InsertResult, InterestProvider, Key, - Metrics, Recon, ReconItem, Server, Store, -}; - -fn start_recon(recon: Recon) -> crate::Client -where - K: Key, - H: AssociativeHash, - S: Store + Send + Sync + 'static, - I: InterestProvider + Send + Sync + 'static, -{ - let mut server = Server::new(recon); - let client = server.client(); - tokio::spawn(server.run()); - client -} - -/// An implementation of a Store that stores keys in an in-memory BTree and throws errors if desired. -#[derive(Clone, Debug)] -pub struct BTreeStoreErrors { - should_error: Arc, - inner: BTreeStore, -} - -impl BTreeStoreErrors { - fn set_error(&self, should_error: bool) { - self.should_error - .store(should_error, std::sync::atomic::Ordering::SeqCst); - } -} - -impl Default for BTreeStoreErrors { - /// By default no errors are thrown. Use set_error to change this. - fn default() -> Self { - Self { - should_error: Arc::new(AtomicBool::new(false)), - inner: BTreeStore::default(), - } - } -} - -#[async_trait] -impl crate::recon::Store for BTreeStoreErrors -where - K: Key, - H: AssociativeHash, -{ - type Key = K; - type Hash = H; - - async fn insert(&self, item: &ReconItem<'_, Self::Key>) -> Result { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: insert") - } else { - self.inner.insert(item).await - } - } - - async fn insert_many(&self, items: &[ReconItem<'_, K>]) -> Result { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: insert_many") - } else { - self.inner.insert_many(items).await - } - } - - async fn hash_range( - &self, - left_fencepost: &Self::Key, - right_fencepost: &Self::Key, - ) -> anyhow::Result> { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: hash_range") - } else { - self.inner.hash_range(left_fencepost, right_fencepost).await - } - } - - async fn range( - &self, - left_fencepost: &Self::Key, - right_fencepost: &Self::Key, - offset: usize, - limit: usize, - ) -> Result + Send + 'static>> { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: range") - } else { - self.inner - .range(left_fencepost, right_fencepost, offset, limit) - .await - } - } - async fn range_with_values( - &self, - left_fencepost: &Self::Key, - right_fencepost: &Self::Key, - offset: usize, - limit: usize, - ) -> Result)> + Send + 'static>> { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: range_with_values") - } else { - self.inner - .range_with_values(left_fencepost, right_fencepost, offset, limit) - .await - } - } - - async fn last( - &self, - left_fencepost: &Self::Key, - right_fencepost: &Self::Key, - ) -> Result> { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: last") - } else { - self.inner.last(left_fencepost, right_fencepost).await - } - } - - async fn first_and_last( - &self, - left_fencepost: &Self::Key, - right_fencepost: &Self::Key, - ) -> Result> { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: first_and_last") - } else { - self.inner - .first_and_last(left_fencepost, right_fencepost) - .await - } - } - - async fn value_for_key(&self, key: &Self::Key) -> Result>> { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: value_for_key") - } else { - self.inner.value_for_key(key).await - } - } - async fn keys_with_missing_values( - &self, - range: RangeOpen, - ) -> Result> { - if self.should_error.load(std::sync::atomic::Ordering::SeqCst) { - bail!("Sorry, bad tree returns errors: keys_with_missing_values") - } else { - self.inner.keys_with_missing_values(range).await - } - } -} - -// use a hackro to avoid setting all the generic types we'd need if using functions -macro_rules! setup_test { - ($alice_store: expr, $alice_interests: expr, $bob_store: expr, $bob_interest: expr,) => {{ - let _ = init_local_tracing(); - - let alice = Recon::new( - $alice_store, - FullInterests::default(), - Metrics::register(&mut Registry::default()), - ); - let alice_client = start_recon(alice); - - let alice_interests = Recon::new( - $alice_interests, - FullInterests::default(), - Metrics::register(&mut Registry::default()), - ); - - let alice_interests_client = start_recon(alice_interests); - - let bob_interest = Recon::new( - $bob_interest, - FullInterests::default(), - Metrics::register(&mut Registry::default()), - ); - let bob_interest_client = start_recon(bob_interest); - - let bob = Recon::new( - $bob_store, - FullInterests::default(), - Metrics::register(&mut Registry::default()), - ); - let bob_client = start_recon(bob); +use crate::test_utils::{MockReconForEventId, MockReconForInterest, PEER_ID}; +use futures::future::poll_immediate; +use libp2p::core::{ConnectedPoint, Endpoint}; +use libp2p::swarm::ConnectionId; +use libp2p::swarm::{behaviour::ConnectionEstablished, NetworkBehaviour}; +use libp2p::Multiaddr; +use libp2p_swarm_test::SwarmExt; +use std::future::poll_fn; +use std::pin::pin; +use std::ptr; +use std::task::{RawWaker, RawWakerVTable, Waker}; - let swarm1 = Swarm::new_ephemeral(|_| { - crate::libp2p::Behaviour::new( - alice_interests_client, - alice_client, - crate::libp2p::Config::default(), - ) - }); - let swarm2 = Swarm::new_ephemeral(|_| { - crate::libp2p::Behaviour::new( - bob_interest_client, - bob_client, - crate::libp2p::Config::default(), - ) - }); - - (swarm1, swarm2) - }}; -} +use crate::libp2p::{stream_set::StreamSet, PeerEvent, PeerStatus}; +use crate::libp2p::{Behaviour, Config}; +use crate::test_utils::TestScaffold; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn in_sync_no_overlap() { - let (mut swarm1, mut swarm2) = setup_test!( - BTreeStoreErrors::default(), - BTreeStoreErrors::default(), - BTreeStoreErrors::default(), - BTreeStoreErrors::default(), - ); + let mut scaffold = TestScaffold::new(false, false); let fut = async move { - let p1 = swarm1.local_peer_id().to_owned(); - let p2 = swarm2.local_peer_id().to_owned(); + let p1 = scaffold.a.swarm.local_peer_id().to_owned(); + let p2 = scaffold.b.swarm.local_peer_id().to_owned(); - swarm1.listen().with_memory_addr_external().await; - swarm2.connect(&mut swarm1).await; + scaffold.a.swarm.listen().with_memory_addr_external().await; + scaffold.b.swarm.connect(&mut scaffold.a.swarm).await; let ([p1_e1, p1_e2, p1_e3, p1_e4], [p2_e1, p2_e2, p2_e3, p2_e4]): ( [crate::libp2p::Event; 4], [crate::libp2p::Event; 4], - ) = libp2p_swarm_test::drive(&mut swarm1, &mut swarm2).await; + ) = libp2p_swarm_test::drive(&mut scaffold.a.swarm, &mut scaffold.b.swarm).await; assert_in_sync(p2, [p1_e1, p1_e2, p1_e3, p1_e4]); assert_in_sync(p1, [p2_e1, p2_e2, p2_e3, p2_e4]); @@ -247,35 +42,40 @@ async fn in_sync_no_overlap() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn initiator_model_error() { - let alice_model_store = BTreeStoreErrors::default(); - alice_model_store.set_error(true); - let (mut swarm1, mut swarm2) = setup_test!( - alice_model_store.clone(), - BTreeStoreErrors::default(), - BTreeStoreErrors::default(), - BTreeStoreErrors::default(), - ); + let mut scaffold = TestScaffold::new(true, false); + + // fail the model sync + let _ = { + scaffold + .a + .interest_store + .as_mock() + .lock() + .await + .expect_hash_range() + .returning(|_, _| Err(anyhow::anyhow!("error"))) + }; let fut = async move { - swarm1.listen().with_memory_addr_external().await; - swarm2.connect(&mut swarm1).await; + scaffold.a.swarm.listen().with_memory_addr_external().await; + scaffold.b.swarm.connect(&mut scaffold.a.swarm).await; let ([p1_e1, p1_e2, p1_e3], [p2_e1, p2_e2, p2_e3]): ( [crate::libp2p::Event; 3], [crate::libp2p::Event; 3], - ) = libp2p_swarm_test::drive(&mut swarm1, &mut swarm2).await; + ) = libp2p_swarm_test::drive(&mut scaffold.a.swarm, &mut scaffold.b.swarm).await; for ev in &[p1_e1, p1_e2, p1_e3, p2_e1, p2_e2, p2_e3] { info!("{:?}", ev); } let ([failed_peer], []): ([crate::libp2p::Event; 1], [crate::libp2p::Event; 0]) = - libp2p_swarm_test::drive(&mut swarm1, &mut swarm2).await; + libp2p_swarm_test::drive(&mut scaffold.a.swarm, &mut scaffold.b.swarm).await; info!("{:?}", failed_peer); assert_eq!( failed_peer, crate::libp2p::Event::PeerEvent(PeerEvent { - remote_peer_id: swarm2.local_peer_id().to_owned(), + remote_peer_id: scaffold.b.swarm.local_peer_id().to_owned(), status: PeerStatus::Failed }) ); @@ -286,35 +86,40 @@ async fn initiator_model_error() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn responder_model_error() { - let bob_model_store = BTreeStoreErrors::default(); - bob_model_store.set_error(true); - let (mut swarm1, mut swarm2) = setup_test!( - BTreeStoreErrors::default(), - BTreeStoreErrors::default(), - bob_model_store.clone(), - BTreeStoreErrors::default(), - ); + let mut scaffold = TestScaffold::new(false, true); + + // fail the model sync + let _ = { + scaffold + .b + .interest_store + .as_mock() + .lock() + .await + .expect_hash_range() + .returning(|_, _| Err(anyhow::anyhow!("error"))) + }; let fut = async move { - swarm1.listen().with_memory_addr_external().await; - swarm2.connect(&mut swarm1).await; + scaffold.a.swarm.listen().with_memory_addr_external().await; + scaffold.b.swarm.connect(&mut scaffold.a.swarm).await; let ([p1_e1, p1_e2, p1_e3], [p2_e1, p2_e2, p2_e3]): ( [crate::libp2p::Event; 3], [crate::libp2p::Event; 3], - ) = libp2p_swarm_test::drive(&mut swarm1, &mut swarm2).await; + ) = libp2p_swarm_test::drive(&mut scaffold.a.swarm, &mut scaffold.b.swarm).await; for ev in &[p1_e1, p1_e2, p1_e3, p2_e1, p2_e2, p2_e3] { info!("{:?}", ev); } let ([], [failed_peer]): ([crate::libp2p::Event; 0], [crate::libp2p::Event; 1]) = - libp2p_swarm_test::drive(&mut swarm1, &mut swarm2).await; + libp2p_swarm_test::drive(&mut scaffold.a.swarm, &mut scaffold.b.swarm).await; info!("{:?}", failed_peer); assert_eq!( failed_peer, crate::libp2p::Event::PeerEvent(PeerEvent { - remote_peer_id: swarm1.local_peer_id().to_owned(), + remote_peer_id: scaffold.a.swarm.local_peer_id().to_owned(), status: PeerStatus::Failed }) ); @@ -367,3 +172,120 @@ fn assert_in_sync(id: PeerId, events: [crate::libp2p::Event; 4]) { } ); } + +fn no_op_waker() -> Waker { + const VTABLE: RawWakerVTable = RawWakerVTable::new( + // Cloning just returns a new no-op raw waker + |_| RAW, + // `wake` does nothing + |_| {}, + // `wake_by_ref` does nothing + |_| {}, + // Dropping does nothing as we don't allocate anything + |_| {}, + ); + const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE); + let waker: Waker = unsafe { Waker::from_raw(RAW) }; + waker +} + +#[tokio::test(flavor = "multi_thread")] +async fn poll_never_resumes() { + let mut behavior = Behaviour::new( + MockReconForInterest::default(), + MockReconForEventId::default(), + Config::default(), + ); + { + let mut pin = pin!(&mut behavior); + assert!(poll_immediate(poll_fn(|cx| pin.poll(cx))).await.is_none()); + } + let address = Multiaddr::empty(); + let role_override = Endpoint::Dialer; + behavior.on_swarm_event(libp2p::swarm::FromSwarm::ConnectionEstablished( + ConnectionEstablished { + peer_id: PEER_ID.parse().unwrap(), + connection_id: ConnectionId::new_unchecked(0), + endpoint: &ConnectedPoint::Dialer { + address, + role_override, + }, + failed_addresses: &[], + other_established: 0, + }, + )); + let mut pin = pin!(&mut behavior); + assert!(poll_immediate(poll_fn(|cx| pin.poll(cx))).await.is_some()); +} + +// #[cfg(loomtest)] +mod loomtest { + use super::*; + use crate::libp2p::{ConnectionInfo, Event, PeerInfo}; + use libp2p::swarm::ToSwarm; + + #[test] + fn poll_loom() { + loom::model(|| { + let mut behavior = Behaviour::new( + MockReconForInterest::default(), + MockReconForEventId::default(), + Config::default(), + ); + let mut behavior = std::pin::Pin::new(&mut behavior); + let waker = no_op_waker(); + let mut cx = std::task::Context::from_waker(&waker); + let _ = behavior.poll(&mut cx); + }); + } + + #[test] + fn poll_with_peer_loom() { + loom::model(|| { + let mut behavior = Behaviour::new( + MockReconForInterest::default(), + MockReconForEventId::default(), + Config::default(), + ); + behavior.peers.insert( + PeerId::random(), + PeerInfo { + status: PeerStatus::Waiting, + connections: vec![ConnectionInfo { + id: ConnectionId::new_unchecked(0), + dialer: true, + }], + last_sync: None, + }, + ); + let mut behavior = std::pin::Pin::new(&mut behavior); + let waker = no_op_waker(); + let mut cx = std::task::Context::from_waker(&waker); + let _ = behavior.poll(&mut cx); + }); + } + + #[test] + fn send_loom() { + loom::model(|| { + let mut behavior = Behaviour::new( + MockReconForInterest::default(), + MockReconForEventId::default(), + Config::default(), + ); + let mut behavior = std::pin::Pin::new(&mut behavior); + let waker = no_op_waker(); + let mut cx = std::task::Context::from_waker(&waker); + let res = behavior.poll(&mut cx); + assert!(res.is_pending()); + + behavior.send_event(ToSwarm::GenerateEvent(Event::PeerEvent(PeerEvent { + remote_peer_id: PeerId::random(), + status: PeerStatus::Waiting, + }))); + let waker = no_op_waker(); + let mut cx = std::task::Context::from_waker(&waker); + let _ = behavior.poll(&mut cx); + }); + } +} diff --git a/recon/src/protocol.rs b/recon/src/protocol.rs index e3a08d076..93b431a62 100644 --- a/recon/src/protocol.rs +++ b/recon/src/protocol.rs @@ -1095,7 +1095,8 @@ where mod tests { use expect_test::expect; - use crate::{tests::AlphaNumBytes, Sha256a}; + use crate::test_utils::AlphaNumBytes; + use crate::Sha256a; use super::*; diff --git a/recon/src/recon.rs b/recon/src/recon.rs index a2c1c4cf4..22c93e29e 100644 --- a/recon/src/recon.rs +++ b/recon/src/recon.rs @@ -1,4 +1,3 @@ -pub mod btreestore; #[cfg(test)] pub mod tests; @@ -310,8 +309,8 @@ where /// A hash with a count of how many values produced the hash. #[derive(Clone, PartialEq, Serialize, Deserialize)] pub struct HashCount { - hash: H, - count: u64, + pub(crate) hash: H, + pub(crate) count: u64, } impl HashCount { @@ -435,12 +434,12 @@ pub trait Store { /// Insert a new key into the key space. Returns true if the key did not exist. /// The value will be updated if included - async fn insert(&self, item: &ReconItem<'_, Self::Key>) -> Result; + async fn insert<'a>(&self, item: &ReconItem<'a, Self::Key>) -> Result; /// Insert new keys into the key space. /// Returns true for each key if it did not previously exist, in the /// same order as the input iterator. - async fn insert_many(&self, items: &[ReconItem<'_, Self::Key>]) -> Result; + async fn insert_many<'a>(&self, items: &[ReconItem<'a, Self::Key>]) -> Result; /// Return the hash of all keys in the range between left_fencepost and right_fencepost. /// Both range bounds are exclusive. @@ -592,11 +591,11 @@ where type Key = K; type Hash = H; - async fn insert(&self, item: &ReconItem<'_, Self::Key>) -> Result { + async fn insert<'a>(&self, item: &ReconItem<'a, Self::Key>) -> Result { self.as_ref().insert(item).await } - async fn insert_many(&self, items: &[ReconItem<'_, Self::Key>]) -> Result { + async fn insert_many<'a>(&self, items: &[ReconItem<'a, Self::Key>]) -> Result { self.as_ref().insert_many(items).await } diff --git a/recon/src/recon/tests.rs b/recon/src/recon/tests.rs index 06fe5b3f8..7dc82b0f7 100644 --- a/recon/src/recon/tests.rs +++ b/recon/src/recon/tests.rs @@ -7,7 +7,7 @@ //! * Display - User facing representation of the data //! * Debug - Compact developer facing representation of the data (i.e. first few chars of a hash) //! * Debug Alternate ({:#?}) - Full debug representation of the data -//! * Pretty - Psuedo sequence diagram representation (used for sequence tests) +//! * Pretty - Psuedo sequence diagram representation (used for sequence test_utils) lalrpop_util::lalrpop_mod!( #[allow(clippy::all, missing_debug_implementations)] @@ -39,11 +39,11 @@ use lalrpop_util::ParseError; use pretty::{Arena, DocAllocator, DocBuilder, Pretty}; use crate::protocol::ReconMessage; +use crate::test_utils::{AlphaNumBytes, BTreeStore}; use crate::{ protocol::{self, InitiatorMessage, ResponderMessage, ValueResponse}, recon::{FullInterests, HashCount, InterestProvider, Range, ReconItem}, - tests::AlphaNumBytes, - AssociativeHash, BTreeStore, Client, Key, Metrics, Recon, Server, Sha256a, Store, + AssociativeHash, Client, Key, Metrics, Recon, Server, Sha256a, Store, }; #[derive(Clone, Default, PartialEq, Serialize, Deserialize)] diff --git a/recon/src/sha256a.rs b/recon/src/sha256a.rs index 0bb1cfddf..d2a3f1e70 100644 --- a/recon/src/sha256a.rs +++ b/recon/src/sha256a.rs @@ -197,7 +197,7 @@ impl Debug for Sha256a { mod tests { use super::*; use crate::recon::AssociativeHash; - use crate::tests::AlphaNumBytes; + use crate::test_utils::AlphaNumBytes; use expect_test::expect; diff --git a/recon/src/recon/btreestore.rs b/recon/src/test_utils/btreestore.rs similarity index 97% rename from recon/src/recon/btreestore.rs rename to recon/src/test_utils/btreestore.rs index 1e8ccabfc..1466b540e 100644 --- a/recon/src/recon/btreestore.rs +++ b/recon/src/test_utils/btreestore.rs @@ -6,7 +6,7 @@ use tokio::sync::Mutex; use crate::recon::{AssociativeHash, Key, MaybeHashedKey, ReconItem, Store}; -use super::{HashCount, InsertResult}; +use crate::recon::{HashCount, InsertResult}; #[derive(Clone, Debug)] struct BTreeStoreInner { @@ -152,7 +152,7 @@ where type Key = K; type Hash = H; - async fn insert(&self, item: &ReconItem<'_, Self::Key>) -> Result { + async fn insert<'a>(&self, item: &ReconItem<'a, Self::Key>) -> Result { let mut inner = self.inner.lock().await; let new = inner .keys @@ -165,7 +165,7 @@ where Ok(new) } - async fn insert_many(&self, items: &[ReconItem<'_, K>]) -> Result { + async fn insert_many<'a>(&self, items: &[ReconItem<'a, K>]) -> Result { let mut new = vec![false; items.len()]; let mut new_val_cnt = 0; for (idx, item) in items.iter().enumerate() { diff --git a/recon/src/test_utils/mocks.rs b/recon/src/test_utils/mocks.rs new file mode 100644 index 000000000..2c9ec014d --- /dev/null +++ b/recon/src/test_utils/mocks.rs @@ -0,0 +1,276 @@ +//! Testing utilities for recon +use crate::{ + protocol::Recon as ProtocolRecon, HashCount, InsertResult, + InterestProvider as ReconInterestProvider, Metrics, ReconItem, Sha256a, Store, SyncState, +}; +use anyhow::Result; +use ceramic_core::{EventId, Interest, RangeOpen}; +use mockall::mock; + +mock! { + + pub EventIdRecon {} + + impl Clone for EventIdRecon { + fn clone(&self) -> Self; + } + + #[async_trait::async_trait] + impl ProtocolRecon for EventIdRecon { + type Key = EventId; + type Hash = Sha256a; + /// Insert a new key into the key space. + async fn insert(&self, key: ::Key, value: Option>) -> Result<()>; + + /// Get all keys in the specified range + async fn range( + &self, + left_fencepost: ::Key, + right_fencepost: ::Key, + offset: usize, + limit: usize, + ) -> Result::Key>>; + + /// Reports total number of keys + async fn len(&self) -> Result; + + /// Reports if the set is empty. + async fn is_empty(&self) -> Result { + Ok(self.len().await? == 0) + } + + /// Retrieve a value associated with a recon key + async fn value_for_key(&self, key: ::Key) -> Result>>; + + /// Report all keys in the range that are missing a value + async fn keys_with_missing_values(&self, range: RangeOpen<::Key>) + -> Result::Key>>; + + /// Reports the interests of this recon instance + async fn interests(&self) -> Result::Key>>>; + + /// Computes the intersection of input interests with the local interests + async fn process_interests( + &self, + interests: Vec::Key>>, + ) -> Result::Key>>>; + + /// Compute an initial hash for the range + async fn initial_range( + &self, + interest: RangeOpen<::Key>, + ) -> Result::Key, Sha256a>>; + + /// Computes a response to a remote range + async fn process_range( + &self, + range: crate::Range<::Key, Sha256a>, + ) -> Result<(SyncState<::Key, Sha256a>, Vec<::Key>)>; + + /// Create a handle to the metrics + fn metrics(&self) -> Metrics; + } +} + +/// Expose mock for recon with event id +pub type MockReconForEventId = MockEventIdRecon; + +mock! { + + pub InterestRecon {} + + impl Clone for InterestRecon { + fn clone(&self) -> Self; + } + + #[async_trait::async_trait] + impl ProtocolRecon for InterestRecon { + type Key = Interest; + type Hash = Sha256a; + /// Insert a new key into the key space. + async fn insert(&self, key: ::Key, value: Option>) -> Result<()>; + + /// Get all keys in the specified range + async fn range( + &self, + left_fencepost: ::Key, + right_fencepost: ::Key, + offset: usize, + limit: usize, + ) -> Result::Key>>; + + /// Reports total number of keys + async fn len(&self) -> Result; + + /// Reports if the set is empty. + async fn is_empty(&self) -> Result { + Ok(self.len().await? == 0) + } + + /// Retrieve a value associated with a recon key + async fn value_for_key(&self, key: ::Key) -> Result>>; + + /// Report all keys in the range that are missing a value + async fn keys_with_missing_values(&self, range: RangeOpen<::Key>) + -> Result::Key>>; + + /// Reports the interests of this recon instance + async fn interests(&self) -> Result::Key>>>; + + /// Computes the intersection of input interests with the local interests + async fn process_interests( + &self, + interests: Vec::Key>>, + ) -> Result::Key>>>; + + /// Compute an initial hash for the range + async fn initial_range( + &self, + interest: RangeOpen<::Key>, + ) -> Result::Key, Sha256a>>; + + /// Computes a response to a remote range + async fn process_range( + &self, + range: crate::Range<::Key, Sha256a>, + ) -> Result<(SyncState<::Key, Sha256a>, Vec<::Key>)>; + + /// Create a handle to the metrics + fn metrics(&self) -> Metrics; + } +} + +/// Expose mock for recon with interest +pub type MockReconForInterest = MockInterestRecon; + +mock! { + pub InterestInterestProvider {} + + impl Clone for InterestInterestProvider { + fn clone(&self) -> Self; + } + + #[async_trait::async_trait] + impl ReconInterestProvider for InterestInterestProvider { + type Key = Interest; + + async fn interests(&self) -> Result>>; + } +} + +/// Expose mock for interest provider with interest +pub type MockInterestProviderForInterest = MockInterestInterestProvider; + +mock! { + pub EventIdInterestProvider {} + + impl Clone for EventIdInterestProvider { + fn clone(&self) -> Self; + } + + #[async_trait::async_trait] + impl ReconInterestProvider for EventIdInterestProvider { + type Key = EventId; + + async fn interests(&self) -> Result>>; + } +} + +/// Expose mock for interest provider with event id +pub type MockInterestProviderForEventId = MockEventIdInterestProvider; + +mock! { + pub EventIdStore {} + + impl Clone for EventIdStore { + fn clone(&self) -> Self; + } + + #[async_trait::async_trait] + impl Store for EventIdStore { + type Key = EventId; + type Hash = Sha256a; + + async fn insert<'a>(&self, item: &ReconItem<'a, EventId>) -> Result; + + async fn insert_many<'a>(&self, items: &[ReconItem<'a, EventId>]) -> Result; + + async fn hash_range( + &self, + left_fencepost: &EventId, + right_fencepost: &EventId, + ) -> Result>; + + async fn range( + &self, + left_fencepost: &EventId, + right_fencepost: &EventId, + offset: usize, + limit: usize, + ) -> Result + Send + 'static>>; + + async fn range_with_values( + &self, + left_fencepost: &EventId, + right_fencepost: &EventId, + offset: usize, + limit: usize, + ) -> Result)> + Send + 'static>>; + + async fn value_for_key(&self, key: &EventId) -> Result>>; + + async fn keys_with_missing_values(&self, range: RangeOpen) + -> Result>; + } +} + +/// Expose mock for store with event id +pub type MockStoreForEventId = MockEventIdStore; + +mock! { + pub InterestStore {} + + impl Clone for InterestStore { + fn clone(&self) -> Self; + } + + #[async_trait::async_trait] + impl Store for InterestStore { + type Key = Interest; + type Hash = Sha256a; + + async fn insert<'a>(&self, item: &ReconItem<'a, Interest>) -> Result; + + async fn insert_many<'a>(&self, items: &[ReconItem<'a, Interest>]) -> Result; + + async fn hash_range( + &self, + left_fencepost: &Interest, + right_fencepost: &Interest, + ) -> Result>; + + async fn range( + &self, + left_fencepost: &Interest, + right_fencepost: &Interest, + offset: usize, + limit: usize, + ) -> Result + Send + 'static>>; + + async fn range_with_values( + &self, + left_fencepost: &Interest, + right_fencepost: &Interest, + offset: usize, + limit: usize, + ) -> Result)> + Send + 'static>>; + + async fn value_for_key(&self, key: &Interest) -> Result>>; + + async fn keys_with_missing_values(&self, range: RangeOpen) + -> Result>; + } +} + +/// Expose mock for store with event id +pub type MockStoreForInterest = MockInterestStore; diff --git a/recon/src/tests.rs b/recon/src/test_utils/mod.rs similarity index 80% rename from recon/src/tests.rs rename to recon/src/test_utils/mod.rs index d09b6cf77..8fa6c9cdd 100644 --- a/recon/src/tests.rs +++ b/recon/src/test_utils/mod.rs @@ -1,16 +1,31 @@ +mod btreestore; +mod mocks; +mod test_scaffold; +mod test_swarm; + +pub use btreestore::BTreeStore; +pub use mocks::{ + MockInterestProviderForEventId, MockInterestProviderForInterest, MockReconForEventId, + MockReconForInterest, MockStoreForEventId, MockStoreForInterest, +}; +pub use test_scaffold::TestScaffold; + +use crate::libp2p::{Event, PeerEvent, PeerStatus}; +use crate::Key; +use anyhow::Result; +use libp2p::swarm::ToSwarm; use serde::de::Error; use serde::{ de::{self, Visitor}, Deserialize, Serialize, }; -use crate::Key; - /// Sequence of byte values including only ASCII alpha numeric values. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct AlphaNumBytes(Vec); impl AlphaNumBytes { + /// convert to bytes pub fn into_inner(self) -> Vec { self.0 } @@ -133,3 +148,14 @@ impl Key for AlphaNumBytes { self == &Self::min_value() || self == &Self::max_value() } } + +/// Peer ID for testing +pub const PEER_ID: &str = "12D3KooWJqb7KjjcWSC92xcSHhdGUrnJ5FJiTHHdZEW7QaLWG5X3"; + +/// Generate a peer event +pub fn peer_event() -> crate::libp2p::ToSwarmEvent { + ToSwarm::GenerateEvent(Event::PeerEvent(PeerEvent { + remote_peer_id: PEER_ID.parse().unwrap(), + status: PeerStatus::Waiting, + })) +} diff --git a/recon/src/test_utils/test_scaffold.rs b/recon/src/test_utils/test_scaffold.rs new file mode 100644 index 000000000..7b4079970 --- /dev/null +++ b/recon/src/test_utils/test_scaffold.rs @@ -0,0 +1,21 @@ +use crate::test_utils::test_swarm::TestSwarm; +use ceramic_metrics::init_local_tracing; + +/// Container for multiple swarm testing +pub struct TestScaffold { + /// First swarm + pub a: TestSwarm, + /// Second swarm + pub b: TestSwarm, +} + +impl TestScaffold { + pub fn new(mock_a: bool, mock_b: bool) -> Self { + let _ = init_local_tracing(); + + let a = TestSwarm::new(mock_a); + let b = TestSwarm::new(mock_b); + + TestScaffold { a, b } + } +} diff --git a/recon/src/test_utils/test_swarm.rs b/recon/src/test_utils/test_swarm.rs new file mode 100644 index 000000000..82bfc911e --- /dev/null +++ b/recon/src/test_utils/test_swarm.rs @@ -0,0 +1,350 @@ +use crate::libp2p::Behaviour; +use crate::test_utils::{BTreeStore, MockStoreForEventId, MockStoreForInterest}; +use crate::{ + Client, FullInterests, InterestProvider, Key, Metrics, Range, Sha256a, Store, SyncState, +}; +use anyhow::Result; +use ceramic_core::{EventId, Interest, RangeOpen}; +use libp2p_swarm_test::SwarmExt; +use prometheus_client::registry::Registry; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; +use tokio::sync::Mutex; + +#[async_trait::async_trait] +impl crate::protocol::Recon for Arc> +where + K: Key + std::fmt::Debug + Serialize + for<'de> Deserialize<'de>, + R: crate::protocol::Recon, +{ + type Key = K; + type Hash = Sha256a; + + async fn insert(&self, key: Self::Key, value: Option>) -> Result<()> { + self.lock().await.insert(key, value).await + } + + async fn range( + &self, + left_fencepost: Self::Key, + right_fencepost: Self::Key, + offset: usize, + limit: usize, + ) -> Result> { + self.lock() + .await + .range(left_fencepost, right_fencepost, offset, limit) + .await + } + + async fn len(&self) -> Result { + self.lock().await.len().await + } + + async fn value_for_key(&self, key: Self::Key) -> Result>> { + self.lock().await.value_for_key(key).await + } + + async fn keys_with_missing_values( + &self, + range: RangeOpen, + ) -> Result> { + self.lock().await.keys_with_missing_values(range).await + } + + async fn interests(&self) -> Result>> { + self.lock().await.interests().await + } + + async fn process_interests( + &self, + interests: Vec>, + ) -> Result>> { + self.lock().await.process_interests(interests).await + } + + async fn initial_range( + &self, + interest: RangeOpen, + ) -> Result> { + self.lock().await.initial_range(interest).await + } + + async fn process_range( + &self, + range: Range, + ) -> Result<(SyncState, Vec)> { + self.lock().await.process_range(range).await + } + + fn metrics(&self) -> Metrics { + Metrics::register(&mut Registry::default()) + } +} + +#[async_trait::async_trait] +impl Store for Arc> +where + K: Key + std::fmt::Debug + Serialize + for<'de> Deserialize<'de>, + S: Store + Send + Sync, +{ + type Key = K; + type Hash = Sha256a; + + async fn insert<'a>(&self, item: &crate::ReconItem<'a, Self::Key>) -> Result { + self.lock().await.insert(item).await + } + + async fn insert_many<'a>( + &self, + items: &[crate::ReconItem<'a, Self::Key>], + ) -> Result { + self.lock().await.insert_many(items).await + } + + async fn hash_range( + &self, + left_fencepost: &Self::Key, + right_fencepost: &Self::Key, + ) -> Result> { + self.lock() + .await + .hash_range(left_fencepost, right_fencepost) + .await + } + + async fn range( + &self, + left_fencepost: &Self::Key, + right_fencepost: &Self::Key, + offset: usize, + limit: usize, + ) -> Result + Send + 'static>> { + self.lock() + .await + .range(left_fencepost, right_fencepost, offset, limit) + .await + } + + async fn range_with_values( + &self, + left_fencepost: &Self::Key, + right_fencepost: &Self::Key, + offset: usize, + limit: usize, + ) -> Result)> + Send + 'static>> { + self.lock() + .await + .range_with_values(left_fencepost, right_fencepost, offset, limit) + .await + } + + async fn value_for_key(&self, key: &Self::Key) -> Result>> { + self.lock().await.value_for_key(key).await + } + + /// Report all keys in the range that are missing a value. + async fn keys_with_missing_values( + &self, + range: RangeOpen, + ) -> Result> { + self.lock().await.keys_with_missing_values(range).await + } +} + +#[derive(Clone)] +pub enum MockOrRealStore +where + K: Key + std::fmt::Debug + Serialize + for<'de> Deserialize<'de>, + S: Store + Send + Sync, +{ + Mock(Arc>), + Real(BTreeStore), +} + +impl MockOrRealStore +where + K: Key + std::fmt::Debug + Serialize + for<'de> Deserialize<'de>, + S: Store + Send + Sync, +{ + pub fn as_mock(&self) -> Arc> { + match self { + Self::Mock(store) => store.clone(), + Self::Real(_) => panic!("Not a mocked store"), + } + } +} + +#[async_trait::async_trait] +impl Store for MockOrRealStore +where + K: Key + std::fmt::Debug + Serialize + for<'de> Deserialize<'de>, + S: Store + Send + Sync, +{ + type Key = K; + type Hash = Sha256a; + + async fn insert<'a>(&self, item: &crate::ReconItem<'a, Self::Key>) -> Result { + match self { + Self::Mock(store) => store.lock().await.insert(item).await, + Self::Real(store) => store.insert(item).await, + } + } + + async fn insert_many<'a>( + &self, + items: &[crate::ReconItem<'a, Self::Key>], + ) -> Result { + match self { + Self::Mock(store) => store.lock().await.insert_many(items).await, + Self::Real(store) => store.insert_many(items).await, + } + } + + async fn hash_range( + &self, + left_fencepost: &Self::Key, + right_fencepost: &Self::Key, + ) -> Result> { + match self { + Self::Mock(store) => { + store + .lock() + .await + .hash_range(left_fencepost, right_fencepost) + .await + } + Self::Real(store) => store.hash_range(left_fencepost, right_fencepost).await, + } + } + + async fn range( + &self, + left_fencepost: &Self::Key, + right_fencepost: &Self::Key, + offset: usize, + limit: usize, + ) -> Result + Send + 'static>> { + match self { + Self::Mock(store) => { + store + .lock() + .await + .range(left_fencepost, right_fencepost, offset, limit) + .await + } + Self::Real(store) => { + store + .range(left_fencepost, right_fencepost, offset, limit) + .await + } + } + } + + async fn range_with_values( + &self, + left_fencepost: &Self::Key, + right_fencepost: &Self::Key, + offset: usize, + limit: usize, + ) -> Result)> + Send + 'static>> { + match self { + Self::Mock(store) => { + store + .lock() + .await + .range_with_values(left_fencepost, right_fencepost, offset, limit) + .await + } + Self::Real(store) => { + store + .range_with_values(left_fencepost, right_fencepost, offset, limit) + .await + } + } + } + + async fn value_for_key(&self, key: &Self::Key) -> Result>> { + match self { + Self::Mock(store) => store.lock().await.value_for_key(key).await, + Self::Real(store) => store.value_for_key(key).await, + } + } + + /// Report all keys in the range that are missing a value. + async fn keys_with_missing_values( + &self, + range: RangeOpen, + ) -> Result> { + match self { + Self::Mock(store) => store.lock().await.keys_with_missing_values(range).await, + Self::Real(store) => store.keys_with_missing_values(range).await, + } + } +} + +/// Container for swarm testing objects +pub struct TestSwarm { + /// Interests used by recon client connects + pub interest_store: MockOrRealStore, + /// Event ids used by recon client connects + pub event_store: MockOrRealStore, + /// Swarm instance + pub swarm: libp2p::Swarm, Client>>, +} + +fn start_recon(recon: crate::Recon) -> crate::Client +where + K: Key, + H: crate::AssociativeHash, + S: crate::Store + Send + Sync + 'static, + I: InterestProvider + Send + Sync + 'static, +{ + let mut server = crate::Server::new(recon); + let client = server.client(); + tokio::spawn(server.run()); + client +} + +impl TestSwarm { + pub fn new(mock_stores: bool) -> Self { + let event_store = if mock_stores { + MockOrRealStore::Mock(Arc::new(Mutex::new(MockStoreForEventId::new()))) + } else { + MockOrRealStore::Real(BTreeStore::default()) + }; + let event_recon = crate::Recon::new( + event_store.clone(), + FullInterests::default(), + Metrics::register(&mut Registry::default()), + ); + let event_client = start_recon(event_recon); + + let interest_store = if mock_stores { + MockOrRealStore::Mock(Arc::new(Mutex::new(MockStoreForInterest::new()))) + } else { + MockOrRealStore::Real(BTreeStore::default()) + }; + let interest_recon = crate::Recon::new( + interest_store.clone(), + FullInterests::default(), + Metrics::register(&mut Registry::default()), + ); + + let interest_client = start_recon(interest_recon); + + let swarm = libp2p::Swarm::new_ephemeral(|_| { + Behaviour::new( + interest_client, + event_client, + crate::libp2p::Config::default(), + ) + }); + + Self { + interest_store, + event_store, + swarm, + } + } +} diff --git a/store/src/metrics.rs b/store/src/metrics.rs index 176be31eb..b7723fbd5 100644 --- a/store/src/metrics.rs +++ b/store/src/metrics.rs @@ -249,7 +249,7 @@ where type Key = K; type Hash = H; - async fn insert(&self, item: &ReconItem<'_, Self::Key>) -> Result { + async fn insert<'a>(&self, item: &ReconItem<'a, Self::Key>) -> Result { let new_val = item.value.is_some(); let new = StoreMetricsMiddleware::::record(&self.metrics, "insert", self.store.insert(item)) @@ -258,7 +258,7 @@ where Ok(new) } - async fn insert_many(&self, items: &[ReconItem<'_, K>]) -> Result { + async fn insert_many<'a>(&self, items: &[ReconItem<'a, K>]) -> Result { let res = StoreMetricsMiddleware::::record( &self.metrics, "insert_many", diff --git a/store/src/sql/event.rs b/store/src/sql/event.rs index 7c81d4061..67780c445 100644 --- a/store/src/sql/event.rs +++ b/store/src/sql/event.rs @@ -495,14 +495,14 @@ impl recon::Store for SqliteEventStore { type Hash = Sha256a; /// Returns true if the key was new. The value is always updated if included - async fn insert(&self, item: &ReconItem<'_, Self::Key>) -> Result { + async fn insert<'a>(&self, item: &ReconItem<'a, Self::Key>) -> Result { let (new, _new_val) = self.insert_item(item).await?; Ok(new) } /// Insert new keys into the key space. /// Returns true if a key did not previously exist. - async fn insert_many(&self, items: &[ReconItem<'_, EventId>]) -> Result { + async fn insert_many<'a>(&self, items: &[ReconItem<'a, EventId>]) -> Result { match items.len() { 0 => Ok(InsertResult::new(vec![], 0)), _ => { diff --git a/store/src/sql/interest.rs b/store/src/sql/interest.rs index 45bef5460..4be6f76ec 100644 --- a/store/src/sql/interest.rs +++ b/store/src/sql/interest.rs @@ -147,7 +147,7 @@ impl recon::Store for SqliteInterestStore { type Hash = Sha256a; /// Returns true if the key was new. The value is always updated if included - async fn insert(&self, item: &ReconItem<'_, Self::Key>) -> Result { + async fn insert<'a>(&self, item: &ReconItem<'a, Self::Key>) -> Result { // interests don't have values, if someone gives us something we throw an error but allow None/vec![] if let Some(val) = item.value.as_ref() { if !val.is_empty() { @@ -161,7 +161,7 @@ impl recon::Store for SqliteInterestStore { /// Insert new keys into the key space. /// Returns true if a key did not previously exist. - async fn insert_many(&self, items: &[ReconItem<'_, Interest>]) -> Result { + async fn insert_many<'a>(&self, items: &[ReconItem<'a, Interest>]) -> Result { match items.len() { 0 => Ok(InsertResult::new(vec![], 0)), _ => {