diff --git a/Cargo.lock b/Cargo.lock index f584133e4..86f0fa464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,6 +303,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -334,11 +344,23 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand", - "futures-lite", + "fastrand 2.2.0", + "futures-lite 2.5.0", "slab", ] +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + [[package]] name = "async-global-executor" version = "2.4.1" @@ -347,32 +369,61 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.3.1", "async-executor", - "async-io", - "async-lock", + "async-io 2.4.0", + "async-lock 3.4.0", "blocking", - "futures-lite", + "futures-lite 2.5.0", "once_cell", ] +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.28", + "slab", + "socket2 0.4.10", + "waker-fn", +] + [[package]] name = "async-io" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ - "async-lock", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite", + "futures-lite 2.5.0", "parking", - "polling", - "rustix", + "polling 3.7.4", + "rustix 0.38.40", "slab", "tracing", "windows-sys 0.59.0", ] +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + [[package]] name = "async-lock" version = "3.4.0" @@ -384,6 +435,53 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-net" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f" +dependencies = [ + "async-io 1.13.0", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.40", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel 2.3.1", + "async-io 2.4.0", + "async-lock 3.4.0", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.3.1", + "futures-lite 2.5.0", + "rustix 0.38.40", + "tracing", +] + [[package]] name = "async-recursion" version = "1.1.1" @@ -395,21 +493,41 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io 2.4.0", + "async-lock 3.4.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.40", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + [[package]] name = "async-std" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" dependencies = [ + "async-attributes", "async-channel 1.9.0", "async-global-executor", - "async-io", - "async-lock", + "async-io 2.4.0", + "async-lock 3.4.0", + "async-process 2.3.0", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite", + "futures-lite 2.5.0", "gloo-timers 0.3.0", "kv-log-macro", "log", @@ -421,6 +539,21 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "async-std-resolver" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa5ee46ec0c518414838d2fdc7dd18f6ba7d934b6e728005c958621da450682d" +dependencies = [ + "async-std", + "async-trait", + "futures-io", + "futures-util", + "hickory-resolver", + "pin-utils", + "socket2 0.5.7", +] + [[package]] name = "async-stm" version = "0.4.0" @@ -843,6 +976,19 @@ dependencies = [ "constant_time_eq 0.3.1", ] +[[package]] +name = "blake3" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "cc", + "cfg-if", + "constant_time_eq 0.3.1", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -879,7 +1025,7 @@ dependencies = [ "async-channel 2.3.1", "async-task", "futures-io", - "futures-lite", + "futures-lite 2.5.0", "piper", ] @@ -1067,6 +1213,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cached" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4dfac631a8e77b2f327f7852bb6172771f5279c4512efe79fad6067b37be3d" +dependencies = [ + "hashbrown 0.11.2", + "once_cell", +] + [[package]] name = "camino" version = "1.1.9" @@ -1190,8 +1346,10 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.52.6", ] @@ -1201,6 +1359,19 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" +[[package]] +name = "cid" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +dependencies = [ + "core2", + "multibase", + "multihash 0.16.3", + "serde", + "unsigned-varint 0.7.2", +] + [[package]] name = "cid" version = "0.10.1" @@ -2279,6 +2450,19 @@ dependencies = [ "regex", ] +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime 2.1.0", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -2642,6 +2826,17 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener" version = "5.3.1" @@ -2731,6 +2926,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.2.0" @@ -2768,7 +2972,7 @@ name = "fendermint_actor_activity_tracker" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fil_actors_evm_shared", "fil_actors_runtime", "frc42_dispatch", @@ -2789,7 +2993,7 @@ name = "fendermint_actor_chainmetadata" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fil_actors_runtime", "frc42_dispatch", "fvm_ipld_amt", @@ -2807,7 +3011,7 @@ name = "fendermint_actor_eam" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fil_actor_eam", "fil_actors_evm_shared", "fil_actors_runtime", @@ -2828,7 +3032,7 @@ name = "fendermint_actor_gas_market_eip1559" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fendermint_actors_api", "fil_actors_evm_shared", "fil_actors_runtime", @@ -2849,7 +3053,7 @@ name = "fendermint_actors" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fendermint_actor_activity_tracker", "fendermint_actor_chainmetadata", "fendermint_actor_eam", @@ -2867,7 +3071,7 @@ name = "fendermint_actors_api" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fil_actors_evm_shared", "fil_actors_runtime", "frc42_dispatch", @@ -2890,7 +3094,7 @@ dependencies = [ "async-stm", "async-trait", "bytes", - "cid", + "cid 0.10.1", "fendermint_abci", "fendermint_actor_gas_market_eip1559", "fendermint_actors_api", @@ -2925,7 +3129,7 @@ dependencies = [ "ipc_ipld_resolver", "k256 0.11.6", "lazy_static", - "libipld", + "libipld 0.16.0", "libp2p", "libp2p-bitswap", "literally", @@ -2935,7 +3139,7 @@ dependencies = [ "paste", "prometheus", "prometheus_exporter", - "prost", + "prost 0.11.9", "quickcheck", "quickcheck_macros", "rand_chacha", @@ -2961,7 +3165,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bytes", - "cid", + "cid 0.10.1", "clap 4.5.21", "fendermint_materializer", "fendermint_vm_actor_interface", @@ -3016,7 +3220,7 @@ dependencies = [ "async-trait", "byteorder", "bytes", - "cid", + "cid 0.10.1", "ethers", "fendermint_actor_gas_market_eip1559", "fendermint_actors_api", @@ -3060,7 +3264,7 @@ dependencies = [ "anyhow", "async-trait", "axum", - "cid", + "cid 0.10.1", "clap 4.5.21", "erased-serde", "ethers", @@ -3162,7 +3366,7 @@ name = "fendermint_rocksdb" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fendermint_storage", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -3182,7 +3386,7 @@ dependencies = [ "async-trait", "base64 0.21.7", "bytes", - "cid", + "cid 0.10.1", "clap 4.5.21", "ethers", "fendermint_crypto", @@ -3193,7 +3397,7 @@ dependencies = [ "fvm_shared", "hex", "lazy_static", - "prost", + "prost 0.11.9", "serde", "serde_json", "tendermint 0.31.1", @@ -3230,7 +3434,7 @@ version = "0.1.0" dependencies = [ "arbitrary", "arbtest", - "cid", + "cid 0.10.1", "ethers", "fendermint_testing", "fvm_ipld_encoding", @@ -3259,7 +3463,7 @@ name = "fendermint_vm_actor_interface" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "ethers", "ethers-core", "fendermint_crypto", @@ -3289,7 +3493,7 @@ name = "fendermint_vm_core" version = "0.1.0" dependencies = [ "arbitrary", - "cid", + "cid 0.10.1", "fnv", "fvm_shared", "lazy_static", @@ -3304,7 +3508,7 @@ dependencies = [ name = "fendermint_vm_encoding" version = "0.1.0" dependencies = [ - "cid", + "cid 0.10.1", "fvm_shared", "ipc-api", "num-traits", @@ -3325,7 +3529,7 @@ version = "0.1.0" dependencies = [ "anyhow", "arbitrary", - "cid", + "cid 0.10.1", "fendermint_actor_eam", "fendermint_crypto", "fendermint_testing", @@ -3357,7 +3561,7 @@ dependencies = [ "async-stm", "async-trait", "base64 0.21.7", - "cid", + "cid 0.10.1", "ethers", "fendermint_actor_activity_tracker", "fendermint_actor_chainmetadata", @@ -3390,7 +3594,7 @@ dependencies = [ "ipc-api", "ipc-observability", "ipc_actors_abis", - "libipld", + "libipld 0.16.0", "merkle-tree-rs", "multihash 0.18.1", "num-traits", @@ -3421,7 +3625,7 @@ dependencies = [ "anyhow", "arbitrary", "blake2b_simd", - "cid", + "cid 0.10.1", "ethers", "ethers-core", "fendermint_crypto", @@ -3449,7 +3653,7 @@ name = "fendermint_vm_resolver" version = "0.1.0" dependencies = [ "async-stm", - "cid", + "cid 0.10.1", "im", "ipc-api", "ipc_ipld_resolver", @@ -3465,7 +3669,7 @@ dependencies = [ "anyhow", "arbitrary", "async-stm", - "cid", + "cid 0.10.1", "dircpy", "fendermint_testing", "fendermint_vm_core", @@ -3502,7 +3706,7 @@ dependencies = [ "async-stm", "async-trait", "bytes", - "cid", + "cid 0.10.1", "clap 4.5.21", "ethers", "fendermint_crypto", @@ -3568,7 +3772,7 @@ checksum = "e0b1448c65c9a054c640fc086e03b730919ca4feca697c34ed3bda9f16aa982f" dependencies = [ "anyhow", "async-std", - "cid", + "cid 0.10.1", "clap 4.5.21", "futures", "fvm_ipld_blockstore", @@ -3585,7 +3789,7 @@ version = "15.0.0-rc1" source = "git+https://github.com/filecoin-project/builtin-actors?tag=v15.0.0#831bf1b9fe281835409c39a8c454a68dd3200bbc" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fil_actors_evm_shared", "fil_actors_runtime", "fvm_ipld_blockstore", @@ -3623,7 +3827,7 @@ dependencies = [ "blake2b_simd", "byteorder", "castaway", - "cid", + "cid 0.10.1", "fvm_ipld_amt", "fvm_ipld_bitfield", "fvm_ipld_blockstore", @@ -3833,7 +4037,6 @@ dependencies = [ [[package]] name = "frc42_dispatch" version = "7.0.0" -source = "git+https://github.com/filecoin-project/actors-utils?rev=0f8365151f44785f7bead4e5500258fd5c8944e6#0f8365151f44785f7bead4e5500258fd5c8944e6" dependencies = [ "frc42_hasher", "frc42_macros", @@ -3846,7 +4049,6 @@ dependencies = [ [[package]] name = "frc42_hasher" version = "5.0.0" -source = "git+https://github.com/filecoin-project/actors-utils?rev=0f8365151f44785f7bead4e5500258fd5c8944e6#0f8365151f44785f7bead4e5500258fd5c8944e6" dependencies = [ "fvm_sdk", "fvm_shared", @@ -3856,13 +4058,13 @@ dependencies = [ [[package]] name = "frc42_macros" version = "5.0.0" -source = "git+https://github.com/filecoin-project/actors-utils?rev=0f8365151f44785f7bead4e5500258fd5c8944e6#0f8365151f44785f7bead4e5500258fd5c8944e6" dependencies = [ "blake2b_simd", "frc42_hasher", "proc-macro2", "quote", "syn 1.0.109", + "trybuild", ] [[package]] @@ -3940,13 +4142,28 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-lite" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ - "fastrand", + "fastrand 2.2.0", "futures-core", "futures-io", "parking", @@ -4045,7 +4262,7 @@ dependencies = [ "ambassador", "anyhow", "arbitrary", - "cid", + "cid 0.10.1", "derive_more", "filecoin-proofs-api", "fvm-wasm-instrument", @@ -4091,7 +4308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fea333475130094f27ce67809aae3f69eb5247541d835950b7c5da733dbbb34" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fvm_ipld_blockstore", "fvm_ipld_encoding", "itertools 0.11.0", @@ -4119,7 +4336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d064b957420f5ecc137a153baaa6c32e2eb19b674135317200b6f2537eabdbfd" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "multihash 0.18.1", ] @@ -4129,7 +4346,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6190f03442b67b21a3d4e115c4d4dd3468aed24e27ebb074218822c1b3df41ba" dependencies = [ - "cid", + "cid 0.10.1", "futures", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -4145,7 +4362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90608092e31d9a06236268c58f7c36668ab4b2a48afafe3a97e08f094ad7ae50" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fvm_ipld_blockstore", "multihash 0.18.1", "serde", @@ -4163,11 +4380,11 @@ checksum = "48c900736087ff87cc51f669eee2f8e000c80717472242eb3f712aaa059ac3b3" dependencies = [ "anyhow", "byteorder", - "cid", + "cid 0.10.1", "forest_hash_utils", "fvm_ipld_blockstore", "fvm_ipld_encoding", - "libipld-core", + "libipld-core 0.16.0", "multihash 0.18.1", "once_cell", "serde", @@ -4181,7 +4398,7 @@ version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2df521d41de41c34ac712720abdd6e29e68e3ed922554db70dbf8bc49a82b7" dependencies = [ - "cid", + "cid 0.10.1", "fvm_ipld_encoding", "fvm_shared", "lazy_static", @@ -4201,7 +4418,7 @@ dependencies = [ "bitflags 2.6.0", "blake2b_simd", "bls-signatures 0.15.0", - "cid", + "cid 0.10.1", "data-encoding", "data-encoding-macro", "filecoin-proofs-api", @@ -4232,11 +4449,12 @@ dependencies = [ [[package]] name = "gcra" version = "0.4.0" -source = "git+https://github.com/consensus-shipyard/gcra-rs.git?branch=main#621a45559a1107778dfcb6ebdf00a7ee848a8d8c" dependencies = [ + "chrono", "dashmap", "rustc-hash 1.1.0", "thiserror 1.0.69", + "tokio", ] [[package]] @@ -4374,6 +4592,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + [[package]] name = "hashbrown" version = "0.12.3" @@ -4533,7 +4757,7 @@ dependencies = [ "ipnet", "once_cell", "rand", - "socket2", + "socket2 0.5.7", "thiserror 1.0.69", "tinyvec", "tokio", @@ -4693,7 +4917,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -4977,7 +5201,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" dependencies = [ - "async-io", + "async-io 2.4.0", "core-foundation", "fnv", "futures", @@ -4985,6 +5209,7 @@ dependencies = [ "ipnet", "log", "rtnetlink", + "smol", "system-configuration", "tokio", "windows", @@ -5120,6 +5345,17 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "iowrap" version = "0.2.1" @@ -5134,7 +5370,7 @@ name = "ipc-api" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "ethers", "fil_actors_runtime", "fnv", @@ -5168,7 +5404,7 @@ dependencies = [ "async-trait", "base64 0.21.7", "bytes", - "cid", + "cid 0.10.1", "clap 4.5.21", "clap_complete", "env_logger 0.10.2", @@ -5231,7 +5467,7 @@ dependencies = [ "async-trait", "base64 0.21.7", "bytes", - "cid", + "cid 0.10.1", "dirs", "ethers", "ethers-contract", @@ -5276,7 +5512,7 @@ name = "ipc-types" version = "0.1.0" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fvm_ipld_amt", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -5342,7 +5578,7 @@ dependencies = [ "base64 0.21.7", "blake2b_simd", "bloom", - "cid", + "cid 0.10.1", "env_logger 0.10.2", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -5353,7 +5589,7 @@ dependencies = [ "ipc-observability", "ipc_ipld_resolver", "lazy_static", - "libipld", + "libipld 0.16.0", "libp2p", "libp2p-bitswap", "libp2p-mplex", @@ -5377,7 +5613,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.7", "widestring", "windows-sys 0.48.0", "winreg", @@ -5626,6 +5862,24 @@ version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +[[package]] +name = "libipld" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9c3aa309c260aa2f174bac968901eddc546e9d85950c28eae6a7bec402f926" +dependencies = [ + "async-trait", + "cached", + "fnv", + "libipld-cbor 0.14.0", + "libipld-core 0.14.0", + "libipld-macro 0.14.0", + "log", + "multihash 0.16.3", + "parking_lot", + "thiserror 1.0.69", +] + [[package]] name = "libipld" version = "0.16.0" @@ -5633,14 +5887,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1ccd6b8ffb3afee7081fcaec00e1b099fd1c7ccf35ba5729d88538fcc3b4599" dependencies = [ "fnv", - "libipld-cbor", - "libipld-core", - "libipld-macro", + "libipld-cbor 0.16.0", + "libipld-core 0.16.0", + "libipld-macro 0.16.0", "log", "multihash 0.18.1", "thiserror 1.0.69", ] +[[package]] +name = "libipld-cbor" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd1ab68c9d26f20c7d0dfea6eecbae8c00359875210001b33ca27d4a02f3d09" +dependencies = [ + "byteorder", + "libipld-core 0.14.0", + "thiserror 1.0.69", +] + [[package]] name = "libipld-cbor" version = "0.16.0" @@ -5648,7 +5913,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77d98c9d1747aa5eef1cf099cd648c3fd2d235249f5fed07522aaebc348e423b" dependencies = [ "byteorder", - "libipld-core", + "libipld-core 0.16.0", + "thiserror 1.0.69", +] + +[[package]] +name = "libipld-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44790246ec6b7314cba745992c23d479d018073e66d49ae40ae1b64e5dd8eb5" +dependencies = [ + "anyhow", + "cid 0.8.6", + "core2", + "multibase", + "multihash 0.16.3", "thiserror 1.0.69", ] @@ -5659,7 +5938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5acd707e8d8b092e967b2af978ed84709eaded82b75effe6cb6f6cc797ef8158" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "core2", "multibase", "multihash 0.18.1", @@ -5667,13 +5946,22 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "libipld-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852c011562ae5059b67c3a917f9f5945af5a68df8e39ede4444fff33274d25e2" +dependencies = [ + "libipld-core 0.14.0", +] + [[package]] name = "libipld-macro" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71171c54214f866ae6722f3027f81dff0931e600e5a61e6b1b6a49ca0b5ed4ae" dependencies = [ - "libipld-core", + "libipld-core 0.16.0", ] [[package]] @@ -5744,17 +6032,23 @@ dependencies = [ [[package]] name = "libp2p-bitswap" version = "0.25.1" -source = "git+https://github.com/consensus-shipyard/libp2p-bitswap.git?branch=chore-upgrade-libipld#d5da20fd791ee700c9a6c9f32f3df0031598938d" dependencies = [ + "async-std", "async-trait", + "env_logger 0.9.3", "fnv", "futures", "lazy_static", - "libipld", + "libipld 0.14.0", + "libipld 0.16.0", "libp2p", + "multihash 0.16.3", "prometheus", + "prost 0.9.0", + "prost-build", "thiserror 1.0.69", "tracing", + "tracing-subscriber", "unsigned-varint 0.7.2", ] @@ -5805,6 +6099,7 @@ version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d17cbcf7160ff35c3e8e560de4a068fe9d6cb777ea72840e48eb76ff9576c4b6" dependencies = [ + "async-std-resolver", "async-trait", "futures", "hickory-resolver", @@ -5884,6 +6179,7 @@ dependencies = [ "multihash 0.19.2", "quick-protobuf", "rand", + "ring 0.17.8", "serde", "sha2 0.10.8", "thiserror 1.0.69", @@ -5927,6 +6223,8 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49007d9a339b3e1d7eeebc4d67c05dbf23d300b7d091193ec2d3f26802d7faf2" dependencies = [ + "async-io 2.4.0", + "async-std", "data-encoding", "futures", "hickory-proto", @@ -5936,7 +6234,7 @@ dependencies = [ "libp2p-swarm", "rand", "smallvec", - "socket2", + "socket2 0.5.7", "tokio", "tracing", "void", @@ -6046,6 +6344,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c67296ad4e092e23f92aea3d2bdb6f24eab79c0929ed816dfb460ea2f4567d2b" dependencies = [ + "async-std", "bytes", "futures", "futures-timer", @@ -6058,7 +6357,7 @@ dependencies = [ "rand", "ring 0.17.8", "rustls 0.23.16", - "socket2", + "socket2 0.5.7", "thiserror 1.0.69", "tokio", "tracing", @@ -6090,6 +6389,7 @@ version = "0.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80cae6cb75f89dbca53862f9ebe0b9f463aa7b302762fcfaafb9e51dcc9b0f7e" dependencies = [ + "async-std", "either", "fnv", "futures", @@ -6126,13 +6426,14 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b2460fc2748919adff99ecbc1aab296e4579e41f374fb164149bd2c9e529d4c" dependencies = [ + "async-io 1.13.0", "futures", "futures-timer", "if-watch", "libc", "libp2p-core", "libp2p-identity", - "socket2", + "socket2 0.5.7", "tokio", "tracing", ] @@ -6278,6 +6579,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -6401,7 +6708,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix", + "rustix 0.38.40", ] [[package]] @@ -6515,6 +6822,20 @@ dependencies = [ "data-encoding-macro", ] +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "blake3", + "core2", + "digest 0.10.7", + "multihash-derive", + "sha2 0.10.8", + "unsigned-varint 0.7.2", +] + [[package]] name = "multihash" version = "0.18.1" @@ -6561,6 +6882,12 @@ dependencies = [ "synstructure 0.12.6", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "multistream-select" version = "0.13.0" @@ -6671,6 +6998,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" dependencies = [ + "async-io 1.13.0", "bytes", "futures", "libc", @@ -7358,7 +7686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand", + "fastrand 2.2.0", "futures-io", ] @@ -7388,6 +7716,22 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + [[package]] name = "polling" version = "3.7.4" @@ -7398,7 +7742,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix", + "rustix 0.38.40", "tracing", "windows-sys 0.59.0", ] @@ -7576,7 +7920,7 @@ dependencies = [ "hex", "lazy_static", "procfs-core", - "rustix", + "rustix 0.38.40", ] [[package]] @@ -7659,6 +8003,16 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive 0.9.0", +] + [[package]] name = "prost" version = "0.11.9" @@ -7666,7 +8020,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost-build" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" +dependencies = [ + "bytes", + "heck 0.3.3", + "itertools 0.10.5", + "lazy_static", + "log", + "multimap", + "petgraph", + "prost 0.9.0", + "prost-types 0.9.0", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -7682,13 +8069,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" +dependencies = [ + "bytes", + "prost 0.9.0", +] + [[package]] name = "prost-types" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "prost", + "prost 0.11.9", ] [[package]] @@ -7785,6 +8182,8 @@ version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ + "async-io 2.4.0", + "async-std", "bytes", "futures-io", "pin-project-lite", @@ -7792,7 +8191,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.0.0", "rustls 0.23.16", - "socket2", + "socket2 0.5.7", "thiserror 2.0.3", "tokio", "tracing", @@ -7827,7 +8226,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.7", "tracing", "windows-sys 0.59.0", ] @@ -8184,6 +8583,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" dependencies = [ + "async-global-executor", "futures", "log", "netlink-packet-route", @@ -8245,6 +8645,20 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.37.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + [[package]] name = "rustix" version = "0.38.40" @@ -8254,7 +8668,7 @@ dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] @@ -8616,7 +9030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e880e0b1f9c7a8db874642c1217f7e19b29e325f24ab9f0fcb11818adec7f01" dependencies = [ "cbor4ii", - "cid", + "cid 0.10.1", "scopeguard", "serde", ] @@ -8945,6 +9359,23 @@ dependencies = [ "serde", ] +[[package]] +name = "smol" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" +dependencies = [ + "async-channel 1.9.0", + "async-executor", + "async-fs", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-net", + "async-process 1.8.1", + "blocking", + "futures-lite 1.13.0", +] + [[package]] name = "snap" version = "1.1.1" @@ -8968,6 +9399,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socket2" version = "0.5.7" @@ -9362,6 +9803,12 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + [[package]] name = "tempfile" version = "3.14.0" @@ -9369,9 +9816,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.2.0", "once_cell", - "rustix", + "rustix 0.38.40", "windows-sys 0.59.0", ] @@ -9390,8 +9837,8 @@ dependencies = [ "k256 0.13.4", "num-traits", "once_cell", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", "ripemd", "serde", "serde_bytes", @@ -9420,8 +9867,8 @@ dependencies = [ "futures", "num-traits", "once_cell", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", "serde", "serde_bytes", "serde_json", @@ -9473,8 +9920,8 @@ dependencies = [ "flex-error", "num-derive 0.3.3", "num-traits", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", "serde", "serde_bytes", "subtle-encoding", @@ -9491,8 +9938,8 @@ dependencies = [ "flex-error", "num-derive 0.3.3", "num-traits", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", "serde", "serde_bytes", "subtle-encoding", @@ -9713,7 +10160,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.7", "tokio-macros", "windows-sys 0.52.0", ] @@ -9937,7 +10384,7 @@ dependencies = [ "bytes", "futures", "pin-project", - "prost", + "prost 0.11.9", "tendermint 0.31.1", "tendermint-proto 0.31.1", "tokio", @@ -10091,6 +10538,21 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "trybuild" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml 0.8.19", +] + [[package]] name = "tungstenite" version = "0.18.0" @@ -10330,7 +10792,7 @@ version = "1.0.0" source = "git+https://github.com/filecoin-project/builtin-actors?tag=v15.0.0#831bf1b9fe281835409c39a8c454a68dd3200bbc" dependencies = [ "anyhow", - "cid", + "cid 0.10.1", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_ipld_hamt", @@ -10348,6 +10810,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + [[package]] name = "walkdir" version = "2.5.0" @@ -10538,7 +11006,7 @@ dependencies = [ "postcard", "psm", "rayon", - "rustix", + "rustix 0.38.40", "serde", "serde_derive", "smallvec", @@ -10743,6 +11211,18 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.40", +] + [[package]] name = "widestring" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 30c5cc7d1..4232eb781 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,9 @@ members = [ # merkle "ext/merkle-tree-rs", + "ext/gcra-rs", + "ext/libp2p-bitswap", + "ext/frc42_dispatch", # ipc "ipc/cli", @@ -81,7 +84,6 @@ fnv = "1.0" futures = "0.3" futures-core = "0.3" futures-util = "0.3" -gcra = "0.4" hex = "0.4" hex-literal = "0.4.1" http = "0.2.12" @@ -113,8 +115,7 @@ libp2p = { version = "0.53", default-features = false, features = [ "plaintext", ] } libp2p-mplex = { version = "0.41" } -# libp2p-bitswap = "0.25.1" -libp2p-bitswap = { git = "https://github.com/consensus-shipyard/libp2p-bitswap.git", branch = "chore-upgrade-libipld" } # Updated to libipld 0.16 +libp2p-bitswap = { path = "ext/libp2p-bitswap" } libsecp256k1 = "0.7" literally = "0.1.3" log = "0.4" @@ -230,7 +231,7 @@ cid = { version = "0.10.1", default-features = false, features = [ "std", ] } -frc42_dispatch = { git = "https://github.com/filecoin-project/actors-utils", rev = "0f8365151f44785f7bead4e5500258fd5c8944e6" } +frc42_dispatch = { path = "./ext/frc42_dispatch" } # Using the same tendermint-rs dependency as tower-abci. From both we are interested in v037 modules. tower-abci = { version = "0.7" } @@ -244,10 +245,6 @@ tendermint-rpc = { version = "0.31", features = [ ] } tendermint-proto = { version = "0.31" } -[patch.crates-io] -# Use stable-only features. -gcra = { git = "https://github.com/consensus-shipyard/gcra-rs.git", branch = "main" } - [profile.wasm] inherits = "release" panic = "abort" diff --git a/ext/frc42_dispatch/Cargo.toml b/ext/frc42_dispatch/Cargo.toml new file mode 100644 index 000000000..1e7d391e0 --- /dev/null +++ b/ext/frc42_dispatch/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "frc42_dispatch" +description = "Filecoin FRC-0042 calling convention/dispatch support library" +version = "7.0.0" +license = "MIT OR Apache-2.0" +keywords = ["filecoin", "dispatch", "frc-0042"] +repository = "https://github.com/helix-onchain/filecoin/" +edition = "2021" + + +[dependencies] +fvm_ipld_encoding = { workspace = true } +fvm_sdk = { workspace = true, optional = true } +fvm_shared = { workspace = true } +frc42_hasher = { version = "5.0.0", path = "hasher" } +frc42_macros = { version = "5.0.0", path = "macros" } +thiserror = { version = "1.0.31" } + +[features] +# disable default features to avoid dependence on fvm_sdk (for proc macro and similar purposes) +default = ["use_sdk"] +use_sdk = ["dep:fvm_sdk"] diff --git a/ext/frc42_dispatch/README.md b/ext/frc42_dispatch/README.md new file mode 100644 index 000000000..b8ef38f84 --- /dev/null +++ b/ext/frc42_dispatch/README.md @@ -0,0 +1,5 @@ +# frc42_dispatch + +Helper library to work with [FRC-0042](https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0042.md) method hashing + +There's an example of it in use [here](https://github.com/helix-onchain/filecoin/tree/main/dispatch_examples/greeter) \ No newline at end of file diff --git a/ext/frc42_dispatch/generate_hashes.py b/ext/frc42_dispatch/generate_hashes.py new file mode 100644 index 000000000..77b94af98 --- /dev/null +++ b/ext/frc42_dispatch/generate_hashes.py @@ -0,0 +1,27 @@ +from hashlib import blake2b + +# See https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0042.md#method-number-computation +def method_number(name): + name = '1|' + name + hash = blake2b(name.encode('ascii'), digest_size=64) + #print('digest: ' + hash.hexdigest()) + #print(f'{len(hash.digest())} bytes long') + + digest = hash.digest() + while digest: + chunk = digest[:4] + num = int.from_bytes(chunk, byteorder='big') + if num >= 1<<24: + return num + digest = digest[4:] + raise Exception("Method ID could not be determined, please change it") + + +# these are all the method names used in the example token actor +methods = ['Name', 'Symbol', 'TotalSupply', 'BalanceOf', 'Allowance', 'IncreaseAllowance', + 'DecreaseAllowance', 'RevokeAllowance', 'Burn', 'TransferFrom', 'Transfer', 'Mint'] +for method in methods: + num = method_number(method) + #print(f'{num:08x}\t{method}') + # print out Rust code for use in a test + print(f'assert_eq!(method_hash!("{method}"), 0x{num:08x});') \ No newline at end of file diff --git a/ext/frc42_dispatch/hasher/Cargo.toml b/ext/frc42_dispatch/hasher/Cargo.toml new file mode 100644 index 000000000..4b1bd0372 --- /dev/null +++ b/ext/frc42_dispatch/hasher/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "frc42_hasher" +version = "5.0.0" +license = "MIT OR Apache-2.0" +description = "Filecoin FRC-0042 calling convention method hashing" +repository = "https://github.com/helix-onchain/filecoin/" +edition = "2021" + +[dependencies] +fvm_sdk = { workspace = true, optional = true } +fvm_shared = { workspace = true, optional = true } +thiserror = { version = "1.0.31" } + +[features] +# The fvm dependencies are optional. Useful for proc macro and similar purposes. +default = ["use_sdk"] +use_sdk = ["dep:fvm_sdk", "dep:fvm_shared"] diff --git a/ext/frc42_dispatch/hasher/src/hash.rs b/ext/frc42_dispatch/hasher/src/hash.rs new file mode 100644 index 000000000..f40e980fb --- /dev/null +++ b/ext/frc42_dispatch/hasher/src/hash.rs @@ -0,0 +1,230 @@ +use thiserror::Error; + +/// Minimal interface for a hashing function. +/// +/// [`Hasher::hash()`] must return a digest that is at least 4 bytes long so that it can be cast to +/// a [`u32`]. +pub trait Hasher { + /// For an input of bytes return a digest that is at least 4 bytes long. + fn hash(&self, bytes: &[u8]) -> Vec<u8>; +} + +/// Hasher that uses the blake2b hash syscall provided by the FVM. +#[cfg(feature = "use_sdk")] +#[derive(Default)] +pub struct Blake2bSyscall {} + +#[cfg(feature = "use_sdk")] +impl Hasher for Blake2bSyscall { + // fvm_sdk dependence can be removed by setting default-features to false + fn hash(&self, bytes: &[u8]) -> Vec<u8> { + use fvm_shared::crypto::hash::SupportedHashes; + fvm_sdk::crypto::hash_owned(SupportedHashes::Blake2b512, bytes) + } +} + +/// Uses an underlying hashing function (blake2b by convention) to generate method numbers from +/// method names. +#[derive(Default)] +pub struct MethodResolver<T: Hasher> { + hasher: T, +} + +#[derive(Error, PartialEq, Eq, Debug)] +pub enum MethodNameErr { + #[error("empty method name provided")] + EmptyString, + #[error("method name does not conform to the FRC-0042 convention {0}")] + IllegalName(#[from] IllegalNameErr), + #[error("unable to calculate method id, choose a another method name")] + IndeterminableId, +} + +#[derive(Error, PartialEq, Eq, Debug)] +pub enum IllegalNameErr { + #[error("method name doesn't start with capital letter or _")] + NotValidStart, + #[error("method name contains letters outside [a-zA-Z0-9_]")] + IllegalCharacters, +} + +impl<T: Hasher> MethodResolver<T> { + const CONSTRUCTOR_METHOD_NAME: &'static str = "Constructor"; + const CONSTRUCTOR_METHOD_NUMBER: u64 = 1_u64; + const FIRST_METHOD_NUMBER: u64 = 1 << 24; + const DIGEST_CHUNK_LENGTH: usize = 4; + + /// Creates a [`MethodResolver`] with an instance of a hasher (blake2b by convention). + pub fn new(hasher: T) -> Self { + Self { hasher } + } + + /// Generates a standard FRC-0042 compliant method number. + /// + /// The method number is calculated as the first four bytes of `hash(method-name)`. + /// The name `Constructor` is always hashed to 1 and other method names that hash to + /// 0 or 1 are avoided via rejection sampling. + pub fn method_number(&self, method_name: &str) -> Result<u64, MethodNameErr> { + check_method_name(method_name)?; + + if method_name == Self::CONSTRUCTOR_METHOD_NAME { + return Ok(Self::CONSTRUCTOR_METHOD_NUMBER); + } + + let method_name = format!("1|{method_name}"); + let digest = self.hasher.hash(method_name.as_bytes()); + + for chunk in digest.chunks(Self::DIGEST_CHUNK_LENGTH) { + if chunk.len() < Self::DIGEST_CHUNK_LENGTH { + // last chunk may be smaller than 4 bytes + break; + } + + let method_id = as_u32(chunk) as u64; + // Method numbers below FIRST_METHOD_NUMBER are reserved for other use + if method_id >= Self::FIRST_METHOD_NUMBER { + return Ok(method_id); + } + } + + Err(MethodNameErr::IndeterminableId) + } +} + +/// Checks that a method name is valid and compliant with the FRC-0042 standard recommendations. +/// +/// - Only ASCII characters in `[a-zA-Z0-9_]` are allowed. +/// - Starts with a character in `[A-Z_]`. +fn check_method_name(method_name: &str) -> Result<(), MethodNameErr> { + if method_name.is_empty() { + return Err(MethodNameErr::EmptyString); + } + + // Check starts with capital letter + let first_letter = method_name.chars().next().unwrap(); // safe because we checked for empty string + if !(first_letter.is_ascii_uppercase() || first_letter == '_') { + return Err(IllegalNameErr::NotValidStart.into()); + } + + // Check that all characters are legal + if !method_name + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '_') + { + return Err(IllegalNameErr::IllegalCharacters.into()); + } + + Ok(()) +} + +/// Takes a byte array and interprets it as a u32 number. +/// +/// Using big-endian order interperets the first four bytes to an int. +/// +/// The slice passed to this must be at least length 4. +fn as_u32(bytes: &[u8]) -> u32 { + u32::from_be_bytes( + bytes[0..4] + .try_into() + .expect("bytes was not at least length 4"), + ) +} + +#[cfg(test)] +mod tests { + + use super::{Hasher, IllegalNameErr, MethodNameErr, MethodResolver}; + + #[derive(Clone, Copy)] + struct FakeHasher {} + impl Hasher for FakeHasher { + fn hash(&self, bytes: &[u8]) -> Vec<u8> { + bytes.to_vec() + } + } + + #[test] + fn constructor_is_1() { + let method_hasher = MethodResolver::new(FakeHasher {}); + assert_eq!(method_hasher.method_number("Constructor").unwrap(), 1); + } + + #[test] + fn normal_method_is_hashed() { + let fake_hasher = FakeHasher {}; + let method_hasher = MethodResolver::new(fake_hasher); + // note that the method hashing prepends each name with "1|" as a domain separator + assert_eq!( + method_hasher.method_number("NormalMethod").unwrap(), + super::as_u32(&fake_hasher.hash(b"1|NormalMethod")) as u64 + ); + + assert_eq!( + method_hasher.method_number("NormalMethod2").unwrap(), + super::as_u32(&fake_hasher.hash(b"1|NormalMethod2")) as u64 + ); + } + + #[test] + fn disallows_invalid_method_names() { + let method_hasher = MethodResolver::new(FakeHasher {}); + assert_eq!( + method_hasher.method_number("Invalid|Method").unwrap_err(), + MethodNameErr::IllegalName(IllegalNameErr::IllegalCharacters) + ); + assert_eq!( + method_hasher.method_number("").unwrap_err(), + MethodNameErr::EmptyString + ); + assert_eq!( + method_hasher.method_number("invalidMethod").unwrap_err(), + MethodNameErr::IllegalName(IllegalNameErr::NotValidStart) + ); + } + + /// Fake hasher that always returns a digest beginning with b"\0\0\0\0". + #[derive(Clone, Copy)] + struct FakeHasher0 {} + impl Hasher for FakeHasher0 { + fn hash(&self, bytes: &[u8]) -> Vec<u8> { + let mut hash: Vec<u8> = vec![0, 0, 0, 0]; + let mut suffix = bytes.to_vec(); + hash.append(suffix.as_mut()); + hash + } + } + + /// Fake hasher that always returns a digest beginning with b"\0\0\0\1". + #[derive(Clone, Copy)] + struct FakeHasher1 {} + impl Hasher for FakeHasher1 { + fn hash(&self, bytes: &[u8]) -> Vec<u8> { + let mut hash: Vec<u8> = vec![0, 0, 0, 1]; + let mut suffix = bytes.to_vec(); + hash.append(suffix.as_mut()); + hash + } + } + + #[test] + fn avoids_disallowed_method_numbers() { + let hasher_0 = FakeHasher0 {}; + let method_hasher_0 = MethodResolver::new(hasher_0); + + // This simulates a method name that would hash to 0 + let contrived_0 = "MethodName"; + let contrived_0_digest = hasher_0.hash(contrived_0.as_bytes()); + assert_eq!(super::as_u32(&contrived_0_digest), 0); + // But the method number is not a collision + assert_ne!(method_hasher_0.method_number(contrived_0).unwrap(), 0); + + let hasher_1 = FakeHasher1 {}; + let method_hasher_1 = MethodResolver::new(hasher_1); + // This simulates a method name that would hash to 1 + let contrived_1 = "MethodName"; + let contrived_1_digest = hasher_1.hash(contrived_1.as_bytes()); + assert_eq!(super::as_u32(&contrived_1_digest), 1); + // But the method number is not a collision + assert_ne!(method_hasher_1.method_number(contrived_1).unwrap(), 1); + } +} diff --git a/ext/frc42_dispatch/hasher/src/lib.rs b/ext/frc42_dispatch/hasher/src/lib.rs new file mode 100644 index 000000000..ec5d33c10 --- /dev/null +++ b/ext/frc42_dispatch/hasher/src/lib.rs @@ -0,0 +1 @@ +pub mod hash; diff --git a/ext/frc42_dispatch/macros/Cargo.toml b/ext/frc42_dispatch/macros/Cargo.toml new file mode 100644 index 000000000..9a056184b --- /dev/null +++ b/ext/frc42_dispatch/macros/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "frc42_macros" +version = "5.0.0" +license = "MIT OR Apache-2.0" +description = "Filecoin FRC-0042 calling convention procedural macros" +repository = "https://github.com/helix-onchain/filecoin/" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +blake2b_simd = { version = "1.0.0" } +frc42_hasher = { version = "5.0.0", path = "../hasher", default-features = false } +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "1.0", features = ["full"] } + +[dev-dependencies] +trybuild = "1.0" diff --git a/ext/frc42_dispatch/macros/example/Cargo.toml b/ext/frc42_dispatch/macros/example/Cargo.toml new file mode 100644 index 000000000..d3ffbb713 --- /dev/null +++ b/ext/frc42_dispatch/macros/example/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "example" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +frc42_macros = { version = "5.0.0", path = ".." } diff --git a/ext/frc42_dispatch/macros/example/src/main.rs b/ext/frc42_dispatch/macros/example/src/main.rs new file mode 100644 index 000000000..7f3ed17e9 --- /dev/null +++ b/ext/frc42_dispatch/macros/example/src/main.rs @@ -0,0 +1,9 @@ +use frc42_macros::method_hash; + +fn main() { + let str_hash = method_hash!("Method"); + println!("String hash: {str_hash:x}"); + + // this one breaks naming rules and will fail to compile + //println!("error hash: {}", method_hash!("some_function")); +} diff --git a/ext/frc42_dispatch/macros/src/hash.rs b/ext/frc42_dispatch/macros/src/hash.rs new file mode 100644 index 000000000..118201e84 --- /dev/null +++ b/ext/frc42_dispatch/macros/src/hash.rs @@ -0,0 +1,9 @@ +use blake2b_simd::blake2b; +use frc42_hasher::hash::Hasher; + +pub struct Blake2bHasher {} +impl Hasher for Blake2bHasher { + fn hash(&self, bytes: &[u8]) -> Vec<u8> { + blake2b(bytes).as_bytes().to_vec() + } +} diff --git a/ext/frc42_dispatch/macros/src/lib.rs b/ext/frc42_dispatch/macros/src/lib.rs new file mode 100644 index 000000000..40c3eb1b2 --- /dev/null +++ b/ext/frc42_dispatch/macros/src/lib.rs @@ -0,0 +1,62 @@ +use frc42_hasher::hash::MethodResolver; +use proc_macro::TokenStream; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::{parse_macro_input, LitStr, Result}; + +mod hash; +use crate::hash::Blake2bHasher; + +struct MethodName(LitStr); + +impl MethodName { + /// Hash the method name. + fn hash(&self) -> u64 { + let resolver = MethodResolver::new(Blake2bHasher {}); + resolver.method_number(&self.0.value()).unwrap() + } +} + +impl Parse for MethodName { + fn parse(input: ParseStream) -> Result<Self> { + let lookahead = input.lookahead1(); + + if lookahead.peek(LitStr) { + input.parse().map(MethodName) + } else { + Err(lookahead.error()) + } + } +} + +#[proc_macro] +pub fn method_hash(input: TokenStream) -> TokenStream { + let name: MethodName = parse_macro_input!(input); + let hash = name.hash(); + quote!(#hash).into() +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let t = trybuild::TestCases::new(); + t.pass("tests/build-success.rs"); + } + + #[test] + fn empty_names() { + let t = trybuild::TestCases::new(); + // NOTE: these need to live in a separate directory under `tests` + // otherwise cargo tries to build them every time and everything breaks + t.compile_fail("tests/naming/empty-name-string.rs"); + t.compile_fail("tests/naming/missing-name.rs"); + } + + #[test] + fn bad_names() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/naming/illegal-chars.rs"); + t.compile_fail("tests/naming/non-capital-start.rs"); + } +} diff --git a/ext/frc42_dispatch/macros/tests/build-success.rs b/ext/frc42_dispatch/macros/tests/build-success.rs new file mode 100644 index 000000000..4c180543e --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/build-success.rs @@ -0,0 +1,21 @@ +use frc42_macros::method_hash; + +fn main() { + assert_eq!(method_hash!("Method"), 0xa20642fc); + assert_eq!(method_hash!("_Method"), 0xeb9575aa); + + // method names from the example token actor + // numbers are hashed by the python script included in the main dispatch crate + assert_eq!(method_hash!("Name"), 0x02ea015c); + assert_eq!(method_hash!("Symbol"), 0x7adab63e); + assert_eq!(method_hash!("TotalSupply"), 0x06da7a35); + assert_eq!(method_hash!("BalanceOf"), 0x8710e1ac); + assert_eq!(method_hash!("Allowance"), 0xfaa45236); + assert_eq!(method_hash!("IncreaseAllowance"), 0x69ecb918); + assert_eq!(method_hash!("DecreaseAllowance"), 0x5b286f21); + assert_eq!(method_hash!("RevokeAllowance"), 0xa4d840b1); + assert_eq!(method_hash!("Burn"), 0x5584159a); + assert_eq!(method_hash!("TransferFrom"), 0xd7d4deed); + assert_eq!(method_hash!("Transfer"), 0x04cbf732); + assert_eq!(method_hash!("Mint"), 0x06f84ab2); +} diff --git a/ext/frc42_dispatch/macros/tests/naming/empty-name-string.rs b/ext/frc42_dispatch/macros/tests/naming/empty-name-string.rs new file mode 100644 index 000000000..33d56a4a0 --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/empty-name-string.rs @@ -0,0 +1,6 @@ +use frc42_macros::method_hash; + +fn main() { + // this should panic due to empty string + let _str_hash = method_hash!(""); +} diff --git a/ext/frc42_dispatch/macros/tests/naming/empty-name-string.stderr b/ext/frc42_dispatch/macros/tests/naming/empty-name-string.stderr new file mode 100644 index 000000000..64a1c1ec5 --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/empty-name-string.stderr @@ -0,0 +1,7 @@ +error: proc macro panicked + --> tests/naming/empty-name-string.rs:5:18 + | +5 | let _str_hash = method_hash!(""); + | ^^^^^^^^^^^^^^^^ + | + = help: message: called `Result::unwrap()` on an `Err` value: EmptyString diff --git a/ext/frc42_dispatch/macros/tests/naming/illegal-chars.rs b/ext/frc42_dispatch/macros/tests/naming/illegal-chars.rs new file mode 100644 index 000000000..6c0558793 --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/illegal-chars.rs @@ -0,0 +1,6 @@ +use frc42_macros::method_hash; + +fn main() { + // should panic because the name contains illegal chars + let _str_hash = method_hash!("Bad!Method!Name!"); +} diff --git a/ext/frc42_dispatch/macros/tests/naming/illegal-chars.stderr b/ext/frc42_dispatch/macros/tests/naming/illegal-chars.stderr new file mode 100644 index 000000000..766c459b2 --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/illegal-chars.stderr @@ -0,0 +1,7 @@ +error: proc macro panicked + --> tests/naming/illegal-chars.rs:5:18 + | +5 | let _str_hash = method_hash!("Bad!Method!Name!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: called `Result::unwrap()` on an `Err` value: IllegalName(IllegalCharacters) diff --git a/ext/frc42_dispatch/macros/tests/naming/missing-name.rs b/ext/frc42_dispatch/macros/tests/naming/missing-name.rs new file mode 100644 index 000000000..dfc3da6e2 --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/missing-name.rs @@ -0,0 +1,6 @@ +use frc42_macros::method_hash; + +fn main() { + // should panic because no string or identifier provided + let _ident_hash = method_hash!(); +} diff --git a/ext/frc42_dispatch/macros/tests/naming/missing-name.stderr b/ext/frc42_dispatch/macros/tests/naming/missing-name.stderr new file mode 100644 index 000000000..348376580 --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/missing-name.stderr @@ -0,0 +1,7 @@ +error: unexpected end of input, expected string literal + --> tests/naming/missing-name.rs:5:23 + | +5 | let _ident_hash = method_hash!(); + | ^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `method_hash` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/ext/frc42_dispatch/macros/tests/naming/non-capital-start.rs b/ext/frc42_dispatch/macros/tests/naming/non-capital-start.rs new file mode 100644 index 000000000..8ea57d182 --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/non-capital-start.rs @@ -0,0 +1,6 @@ +use frc42_macros::method_hash; + +fn main() { + // should panic because the name starts with non-capital letter + let _str_hash = method_hash!("noPlaceForCamelCase"); +} diff --git a/ext/frc42_dispatch/macros/tests/naming/non-capital-start.stderr b/ext/frc42_dispatch/macros/tests/naming/non-capital-start.stderr new file mode 100644 index 000000000..30ead070d --- /dev/null +++ b/ext/frc42_dispatch/macros/tests/naming/non-capital-start.stderr @@ -0,0 +1,7 @@ +error: proc macro panicked + --> tests/naming/non-capital-start.rs:5:18 + | +5 | let _str_hash = method_hash!("noPlaceForCamelCase"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: called `Result::unwrap()` on an `Err` value: IllegalName(NotValidStart) diff --git a/ext/frc42_dispatch/src/lib.rs b/ext/frc42_dispatch/src/lib.rs new file mode 100644 index 000000000..ad64c36db --- /dev/null +++ b/ext/frc42_dispatch/src/lib.rs @@ -0,0 +1,9 @@ +pub use frc42_hasher as hasher; +pub use frc42_hasher::hash; +pub use frc42_macros::method_hash; + +pub mod match_method; +pub mod message; + +#[cfg(test)] +mod tests {} diff --git a/ext/frc42_dispatch/src/match_method.rs b/ext/frc42_dispatch/src/match_method.rs new file mode 100644 index 000000000..5406d3818 --- /dev/null +++ b/ext/frc42_dispatch/src/match_method.rs @@ -0,0 +1,111 @@ +#[macro_export] +macro_rules! match_method { + ($method:expr, {$($body:tt)*}) => { + match_method!{@match $method, {}, $($body)*} + }; + (@match $method:expr, {$($body:tt)*}, $(,)*) => { + match $method { + $($body)* + } + }; + // matches block with comma + (@match $method:expr, {$($body:tt)*}, $p:literal => $e:expr, $($tail:tt)*) => { + match_method! { + @match + $method, + { + $($body)* + $crate::method_hash!($p) => $e, + }, + $($tail)* + } + }; + // matches block without comma + (@match $method:expr, {$($body:tt)*}, $p:literal => $e:block $($tail:tt)*) => { + match_method! { + @match + $method, + { + $($body)* + $crate::method_hash!($p) => $e, + }, + $($tail)* + } + }; + // matches _ with a trailing comma + (@match $method:expr, {$($body:tt)*}, _ => $e:expr, $($tail:tt)*) => { + match_method! { + @match + $method, + { + $($body)* + _ => $e, + }, + $($tail)* + } + }; + // matches _ without a trailing comma (common if it's the last item) + (@match $method:expr, {$($body:tt)*}, _ => $e:expr) => { + match_method! { + @match + $method, + { + $($body)* + _ => $e, + }, + } + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn handle_constructor() { + let method_num = 1u64; // constructor should always hash to 1 + let ret = match_method!(method_num, { + "Constructor" => Some(1), + _ => None, + }); + + assert_eq!(ret, Some(1)); + } + + #[test] + fn handle_unknown_method() { + let method_num = 12345u64; // not a method we know about + let ret = match_method!(method_num, { + "Constructor" => Some(1), + _ => None, + }); + + assert_eq!(ret, None); + } + + #[test] + fn handle_user_method() { + let method_num = crate::method_hash!("TokensReceived"); + let ret = match_method!(method_num, { + "Constructor" => Some(1), + "TokensReceived" => Some(2), + _ => None, + }); + + assert_eq!(ret, Some(2)); + } + + #[test] + fn handle_optional_commas() { + let method_num = crate::method_hash!("TokensReceived"); + let ret = match_method!(method_num, { + "Constructor" => Some(1), + "TokensReceived" => { + Some(2) + } + _ => { + None + } + }); + + assert_eq!(ret, Some(2)); + } +} diff --git a/ext/frc42_dispatch/src/message.rs b/ext/frc42_dispatch/src/message.rs new file mode 100644 index 000000000..fe274e191 --- /dev/null +++ b/ext/frc42_dispatch/src/message.rs @@ -0,0 +1,65 @@ +use fvm_ipld_encoding::ipld_block::IpldBlock; +#[cfg(feature = "use_sdk")] +use fvm_sdk::send; +use fvm_shared::{address::Address, econ::TokenAmount, error::ErrorNumber, Response}; +use thiserror::Error; + +use crate::hash::{Hasher, MethodNameErr, MethodResolver}; + +/// Utility to invoke standard methods on deployed actors. +#[derive(Default)] +pub struct MethodMessenger<T: Hasher> { + method_resolver: MethodResolver<T>, +} + +#[derive(Error, PartialEq, Eq, Debug)] +pub enum MethodMessengerError { + #[error("error when calculating method name: `{0}`")] + MethodName(#[from] MethodNameErr), + #[error("error sending message: `{0}`")] + Syscall(#[from] ErrorNumber), +} + +impl<T: Hasher> MethodMessenger<T> { + /// Creates a new method messenger using a specified hashing function (blake2b by default). + pub fn new(hasher: T) -> Self { + Self { + method_resolver: MethodResolver::new(hasher), + } + } + + /// Calls a method (by name) on a specified actor by constructing and publishing the underlying + /// on-chain message. + #[cfg(feature = "use_sdk")] + pub fn call_method( + &self, + to: &Address, + method: &str, + params: Option<IpldBlock>, + value: TokenAmount, + ) -> Result<Response, MethodMessengerError> { + let method = self.method_resolver.method_number(method)?; + send::send( + to, + method, + params, + value, + None, + fvm_shared::sys::SendFlags::empty(), + ) + .map_err(MethodMessengerError::from) + } + + #[cfg(not(feature = "use_sdk"))] + #[allow(unused_variables)] + pub fn call_method( + &self, + to: &Address, + method: &str, + params: Option<IpldBlock>, + value: TokenAmount, + ) -> Result<Response, MethodMessengerError> { + let _method = self.method_resolver.method_number(method)?; + unimplemented!() + } +} diff --git a/ext/gcra-rs/.gitignore b/ext/gcra-rs/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/ext/gcra-rs/.gitignore @@ -0,0 +1 @@ +/target diff --git a/ext/gcra-rs/Cargo.lock b/ext/gcra-rs/Cargo.lock new file mode 100644 index 000000000..802afe887 --- /dev/null +++ b/ext/gcra-rs/Cargo.lock @@ -0,0 +1,638 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cxx" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "gcra" +version = "0.4.0" +dependencies = [ + "chrono", + "dashmap", + "rustc-hash", + "thiserror", + "tokio", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[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", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "proc-macro2" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/ext/gcra-rs/Cargo.toml b/ext/gcra-rs/Cargo.toml new file mode 100644 index 000000000..1f2c71a14 --- /dev/null +++ b/ext/gcra-rs/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "gcra" +version = "0.4.0" +edition = "2021" +authors = ["Sam Shih <lytefast@github.com>"] +license = "MIT" +readme = "README.md" +repository = "https://github.com/lytefast/gcra-rs" +homepage = "https://github.com/lytefast/gcra-rs" +description = "A basic implementation of GCRA algorithm for rate limiting" +keywords = ["rate-limit", "rate", "limit", "gcra"] + +[features] +default = ["rate-limiter"] +rate-limiter = ["dashmap", "rustc-hash"] + +[dependencies] +dashmap = { version = "5.4.0", optional = true } +rustc-hash = { version = "1.1.0", optional = true } +thiserror = "1.0.39" + +[dev-dependencies] +chrono = "0.4.23" +tokio = { version = "1.26.0", features = ["full"] } + +[[example]] +name = "rate_limiter" +required-features = ["rate-limiter"] diff --git a/ext/gcra-rs/LICENSE.md b/ext/gcra-rs/LICENSE.md new file mode 100644 index 000000000..9f5fe052e --- /dev/null +++ b/ext/gcra-rs/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Sam Shih + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ext/gcra-rs/README.md b/ext/gcra-rs/README.md new file mode 100644 index 000000000..6105c7c97 --- /dev/null +++ b/ext/gcra-rs/README.md @@ -0,0 +1,55 @@ +[](https://travis-ci.com/lytefast/gcra) +[](LICENSE) +[](https://docs.rs/gcra/) +[](https://crates.io/crates/gcra) + +# GCRA: A basic implementation + +Library which implements the core +[GCRA](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm) functionality in rust. + +## Features + +- `rate-limiter` a LRU + expiring rate limiter. Implements `Send + Sync` so can be used asynchronously. + +## Usage + +```rust +use gcra::{GcraState, RateLimit}; + +fn check_rate_limit() { + const LIMIT: u32 = 1; + // Create a rate limit that allows `1/1s` + let rate_limit = RateLimit::per_sec(LIMIT); + + let mut user_state = GcraState::default(); + assert!(user_state.check_and_modify(&rate_limit, 1).is_ok()); + assert!( + user_state.check_and_modify(&rate_limit, 1).is_err(), + "We should be over the limit now" + ); +} +``` + +### With `rate-limiter` + +```rust +use gcra::{GcraError, RateLimit, RateLimiter}; + +#[tokio::main] +async fn main() -> Result<(), GcraError> { + let rate_limit = RateLimit::per_sec(2); + let mut rl = RateLimiter::new(4); + + rl.check("key", rate_limit.clone(), 1).await?; + rl.check("key", rate_limit.clone(), 1).await?; + + match rl.check("key", rate_limit.clone(), 1).await { + Err(GcraError::DeniedUntil { next_allowed_at }) => { + print!("Denied: Request next at {:?}", next_allowed_at); + Ok(()) + } + unexpected => panic!("Opps something went wrong! {:?}", unexpected), + } +} +``` diff --git a/ext/gcra-rs/examples/gcra_check.rs b/ext/gcra-rs/examples/gcra_check.rs new file mode 100644 index 000000000..58108a684 --- /dev/null +++ b/ext/gcra-rs/examples/gcra_check.rs @@ -0,0 +1,47 @@ +use std::time::Instant; + +use chrono::{DateTime, Duration, Utc}; +use gcra::{GcraError, GcraState, RateLimit}; + +fn check_rate_limit(rate_limit: &RateLimit, gcra_state: &mut GcraState) -> bool { + const COST: u32 = 1; + match gcra_state.check_and_modify(rate_limit, COST) { + Ok(_) => { + println!("allowed"); + true + } + Err(GcraError::DeniedUntil { next_allowed_at }) => { + println!("denied. Try again at {:?}", to_date_time(next_allowed_at)); + false + } + + Err(error) => { + println!("denied: {:?}", error); + false + } + } +} + +fn to_date_time(instant: Instant) -> DateTime<Utc> { + let diff = instant - Instant::now(); + Utc::now() + Duration::from_std(diff).unwrap() +} + +fn main() { + const LIMIT: u32 = 3; + // Create a rate limit that allows `3/1s` + let rate_limit = RateLimit::per_sec(LIMIT); + + let mut user_state = GcraState::default(); + for i in 0..LIMIT { + assert!( + check_rate_limit(&rate_limit, &mut user_state), + "Attempt #{} should be allowed", + i + 1 + ); + } + assert!( + !check_rate_limit(&rate_limit, &mut user_state), + "We should be over the limit now" + ); +} diff --git a/ext/gcra-rs/examples/rate_limiter.rs b/ext/gcra-rs/examples/rate_limiter.rs new file mode 100644 index 000000000..c92268a8d --- /dev/null +++ b/ext/gcra-rs/examples/rate_limiter.rs @@ -0,0 +1,21 @@ +use gcra::{GcraError, RateLimit, RateLimiter}; + +const CACHE_CAPACITY: usize = 4; +const WORKER_SHARD_COUNT: usize = 2; + +#[tokio::main] +async fn main() -> Result<(), GcraError> { + let rate_limit = RateLimit::per_sec(2); + let mut rl = RateLimiter::with_shards(CACHE_CAPACITY, WORKER_SHARD_COUNT); + + rl.check("key", rate_limit.clone(), 1).await?; + rl.check("key", rate_limit.clone(), 1).await?; + + match rl.check("key", rate_limit.clone(), 1).await { + Err(GcraError::DeniedUntil { next_allowed_at }) => { + print!("Denied: Request next at {:?}", next_allowed_at); + Ok(()) + } + unexpected => panic!("Opps something went wrong! {:?}", unexpected), + } +} diff --git a/ext/gcra-rs/src/gcra.rs b/ext/gcra-rs/src/gcra.rs new file mode 100644 index 000000000..8e362130e --- /dev/null +++ b/ext/gcra-rs/src/gcra.rs @@ -0,0 +1,489 @@ +use std::time::Instant; +use thiserror::Error; + +use crate::rate_limit::RateLimit; + +#[derive(Error, Debug, PartialEq, Eq)] +pub enum GcraError { + /// Cost of the increment exceeds the rate limit and will never succeed + #[error("Cost of the increment ({cost}) exceeds the rate limit ({rate_limit:?}) and will never succeed)")] + DeniedIndefinitely { cost: u32, rate_limit: RateLimit }, + /// Limited request until after the [Instant] + #[error("Denied until {next_allowed_at:?}")] + DeniedUntil { next_allowed_at: Instant }, +} + +/// Holds the minmum amount of state necessary to implement a GRCA leaky buckets. +/// Refer to: [understanding GCRA](https://blog.ian.stapletoncordas.co/2018/12/understanding-generic-cell-rate-limiting.html) +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub struct GcraState { + /// GCRA's Theoretical Arrival Time (**TAT**) + /// An unset value signals a new state + pub tat: Option<Instant>, +} + +impl GcraState { + /// Check if we are allowed to proceed. If so updated our internal state and return true. + /// + /// Simply passes the current Instant to [`check_and_modify_at()`] + #[inline] + pub fn check_and_modify(&mut self, rate_limit: &RateLimit, cost: u32) -> Result<(), GcraError> { + let arrived_at = Instant::now(); + self.check_and_modify_at(rate_limit, arrived_at, cost) + } + + /// Check if we are allowed to proceed at the given arrival time. + /// If so updated our internal state and return true. + /// Explaination of GCRA can be found [here](https://blog.ian.stapletoncordas.co/2018/12/understanding-generic-cell-rate-limiting.html) + /// + /// # Returns + /// If denied, will return an [Result::Err] where the value is the next allowed timestamp. + pub fn check_and_modify_at( + &mut self, + rate_limit: &RateLimit, + arrived_at: Instant, + cost: u32, + ) -> Result<(), GcraError> { + let increment_interval = rate_limit.increment_interval(cost); + + let compute_tat = |new_tat: Instant| { + if increment_interval > rate_limit.period { + return Err(GcraError::DeniedIndefinitely { + cost, + rate_limit: rate_limit.clone(), + }); + } + + Ok(new_tat + increment_interval) + }; + + let tat = match self.tat { + Some(tat) => tat, + None => { + // First ever request. Allow passage and update self. + self.tat = Some(compute_tat(arrived_at)?); + return Ok(()); + } + }; + + // We had a previous request + if tat < arrived_at { + // prev request was really old + let new_tat = std::cmp::max(tat, arrived_at); + self.tat = Some(compute_tat(new_tat)?); + Ok(()) + } else { + // prev request was recent and there's a possibility that we've reached the limit + let delay_variation_tolerance = rate_limit.period; + let new_tat = compute_tat(tat)?; + + let next_allowed_at = new_tat - delay_variation_tolerance; + if next_allowed_at <= arrived_at { + self.tat = Some(new_tat); + Ok(()) + } else { + // Denied, must wait until next_allowed_at + Err(GcraError::DeniedUntil { next_allowed_at }) + } + } + } + + /// Reverts rate_limit by cost, and updated our internal state. + /// + /// Simply passes the current Instant to [`revert_at()`] + #[inline] + pub fn revert(&mut self, rate_limit: &RateLimit, cost: u32) -> Result<(), GcraError> { + let arrived_at = Instant::now(); + self.revert_at(rate_limit, arrived_at, cost) + } + + /// Reverts rate_limit by cost, and updated our internal state. + /// + /// This is a hack that substracts the incremental cost from the TAT. + pub fn revert_at( + &mut self, + rate_limit: &RateLimit, + arrived_at: Instant, + cost: u32, + ) -> Result<(), GcraError> { + let increment_interval = rate_limit.increment_interval(cost); + + let compute_revert_tat = |new_tat: Instant| new_tat - increment_interval; + + let tat = match self.tat { + Some(tat) => tat, + None => { + // First ever request. Nothing to do. + return Ok(()); + } + }; + + // We had a previous request + if tat < arrived_at { + // Reset state: prev request was really old + self.tat = None; + } else { + // prev request was recent + self.tat = Some(compute_revert_tat(tat)); + } + Ok(()) + } + + pub fn remaining_resources(&self, rate_limit: &RateLimit, now: Instant) -> u32 { + if rate_limit.period.is_zero() { + return 0; + } + + let time_to_tat = match self.tat.and_then(|tat| tat.checked_duration_since(now)) { + Some(duration_until) => duration_until, + None => return rate_limit.resource_limit, + }; + + // Logically this makes more sense as: + // consumed_resources = time_to_tat * (resource_limit/period) + // but we run it this way because of Duration's arithmetic functions + + // Requires nightly + // let consumed_resources = + // (time_to_tat * rate_limit.resource_limit).div_duration_f32(rate_limit.period); + + let consumed_resources = time_to_tat.as_nanos() as f64 + / rate_limit.period.as_nanos() as f64 + * rate_limit.resource_limit as f64; + + rate_limit.resource_limit - consumed_resources.ceil() as u32 + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use super::*; + + #[test] + fn test_rate_limit_unused_counts() { + let base_tat = Instant::now(); + let rate_limit = RateLimit::new(10, Duration::from_secs(1)); + + assert_eq!( + 4, + GcraState { + tat: Some(base_tat + Duration::from_millis(550)) + } + .remaining_resources(&rate_limit, base_tat), + "Remaining count should ceiled" + ); + assert_eq!( + 0, + GcraState { + tat: Some(base_tat + Duration::from_millis(950)) + } + .remaining_resources(&rate_limit, base_tat), + "Remaining count should ceiled, thus preventing any additional requests" + ); + + assert_eq!( + 9, + GcraState { + tat: Some(base_tat + Duration::from_millis(100)) + } + .remaining_resources(&rate_limit, base_tat), + "Remaining count is based on max_period timeout" + ); + } + + #[test] + fn gcra_basics() { + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(1, Duration::from_secs(1)); + + let first_req_ts = Instant::now(); + assert_eq!( + Ok(()), + gcra.check_and_modify(&rate_limit, 1), + "request #1 should pass" + ); + let after_first_tat = gcra.tat; + assert!( + after_first_tat.is_some(), + "state should be modified and have a TAT in the future" + ); + + let next_allowed_ts = match gcra.check_and_modify(&rate_limit, 1) { + Err(GcraError::DeniedUntil { next_allowed_at }) => next_allowed_at, + _ => panic!("request #2 should be denied temporarily"), + }; + assert!( + next_allowed_ts >= first_req_ts + Duration::from_secs(1), + "we should only be allowed after the burst period" + ); + assert_eq!(after_first_tat, gcra.tat, "State should be unchanged.") + } + + #[test] + fn gcra_limited() { + const LIMIT: u32 = 5; + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(LIMIT, Duration::from_secs(1)); + + let req_ts = Instant::now(); + for i in 0..LIMIT { + assert_eq!( + Ok(()), + gcra.check_and_modify_at(&rate_limit, req_ts, 1), + "request #{} should pass", + i + 1 + ); + } + + assert_eq!( + Some(req_ts + rate_limit.period), + gcra.tat, + "state should be modified and have a TAT for the full period", + ); + + // Trigger another event + let denied_result = gcra.check_and_modify_at(&rate_limit, req_ts, 1); + + assert_eq!( + Some(req_ts + rate_limit.period), + gcra.tat, + "state should not have changed when at limit", + ); + + assert_eq!( + Err(GcraError::DeniedUntil { + next_allowed_at: req_ts + rate_limit.emission_interval + }), + denied_result, + "next request should be denied", + ); + } + + #[test] + fn gcra_revert_new() { + const LIMIT: u32 = 5; + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(LIMIT, Duration::from_secs(1)); + + let req_ts = Instant::now(); + // Revert before any calls + assert!( + gcra.revert_at(&rate_limit, req_ts, 1).is_ok(), + "revert should have released resources" + ); + assert_eq!(None, gcra.tat, "state should not have changed at all",); + } + + #[test] + fn gcra_revert_existing() { + const LIMIT: u32 = 5; + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(LIMIT, Duration::from_secs(1)); + + let req_ts = Instant::now(); + assert_eq!( + Ok(()), + gcra.check_and_modify_at(&rate_limit, req_ts, 5), + "use up all resources", + ); + + assert_eq!( + Some(req_ts + rate_limit.period), + gcra.tat, + "state should be modified and have a TAT for the full period", + ); + + // Revert + assert!( + gcra.revert_at(&rate_limit, req_ts, 1).is_ok(), + "revert should have released resources" + ); + assert_eq!( + Some(req_ts + rate_limit.period - rate_limit.increment_interval(1)), + gcra.tat, + "state should not have changed when at limit", + ); + + // Confirm revert re-enables + assert_eq!( + Ok(()), + gcra.check_and_modify_at(&rate_limit, req_ts, 1), + "additional resources should have been freed", + ); + } + + #[test] + fn gcra_revert_existing_ancient() { + const LIMIT: u32 = 5; + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(LIMIT, Duration::from_secs(1)); + + let past_req_ts = Instant::now() - Duration::from_secs(100); + assert_eq!( + Ok(()), + gcra.check_and_modify_at(&rate_limit, past_req_ts, 5), + "use up all resources, but in distant past", + ); + assert_eq!( + Some(past_req_ts + rate_limit.period), + gcra.tat, + "state should be modified and have a TAT for the past", + ); + + // Revert using current time + let req_ts = Instant::now(); + assert!( + gcra.revert_at(&rate_limit, req_ts, 1).is_ok(), + "revert should have released resources" + ); + assert_eq!( + None, gcra.tat, + "state should have reset since it was so old", + ); + + // Confirm revert had 0 effect + assert_eq!( + Ok(()), + gcra.check_and_modify_at(&rate_limit, req_ts, 1), + "additional resources should have been freed", + ); + assert_eq!( + gcra.tat, + Some(req_ts + rate_limit.increment_interval(1)), + "new TAT state should have been moved forward according to cost like normal" + ); + } + + #[test] + fn gcra_leaky() { + // const INCREMENT_INTERVAL: u64 = 500; + const INCREMENT_INTERVAL: Duration = Duration::from_millis(500); + + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(10, 10 * INCREMENT_INTERVAL); + assert_eq!(INCREMENT_INTERVAL, rate_limit.emission_interval); + + let arrived_at = Instant::now(); + assert_eq!( + Ok(()), + gcra.check_and_modify_at(&rate_limit, arrived_at, 1), + "request #1 should pass" + ); + assert_eq!( + gcra.tat, + Some(arrived_at + INCREMENT_INTERVAL), + "new TAT state should have been moved forward according to cost" + ); + + assert_eq!( + Ok(()), + gcra.check_and_modify(&rate_limit, 9), + "request #2 should consume all remaining resources and pass" + ); + assert!( + matches!(gcra.check_and_modify(&rate_limit, 1), Err(_allowed_at)), + "request #3 should fail since all resources consumed" + ); + + let current_tat = gcra.tat.expect("should have a tat state after use"); + assert!(current_tat > Instant::now(), "tat in the future"); + + assert!( + matches!( + // manually force time check that we know will fail + gcra.check_and_modify_at( + &rate_limit, + current_tat - rate_limit.period - Duration::from_millis(1), + 1 + ), + Err(_allowed_at) + ), + "request #4 before leak period should fail. INCREMENT_INTERVAL has not passed yet." + ); + + assert!( + matches!( + gcra.check_and_modify_at(&rate_limit, current_tat - rate_limit.period, 1), + Err(_allowed_at) + ), + "request #5 after leak period should pass. INCREMENT_INTERVAL has passed" + ); + } + + #[test] + fn gcra_cost_indefinitely_denied() { + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(5, Duration::from_secs(1)); + + assert_eq!( + Ok(()), + gcra.check_and_modify(&rate_limit, 1), + "request #1 should pass" + ); + + let over_limit_cost = rate_limit.resource_limit + 1; + match gcra.check_and_modify(&rate_limit, over_limit_cost) { + Err(GcraError::DeniedIndefinitely { + cost, + rate_limit: rl, + }) => { + assert_eq!(over_limit_cost, cost); + assert_eq!(rate_limit, rl); + } + e => panic!("request #2 would never succeed {:?}", e), + }; + } + + #[test] + fn gcra_cost_temporarily_denied() { + let mut gcra = GcraState::default(); + let rate_limit = RateLimit::new(5, Duration::from_secs(1)); + + let first_req_ts = Instant::now(); + assert_eq!( + Ok(()), + gcra.check_and_modify(&rate_limit, 1), + "request #1 should pass" + ); + + let after_first_tat = gcra.tat; + assert!( + after_first_tat.is_some(), + "state should be modified and have a TAT in the future" + ); + + let next_allowed_ts = match gcra.check_and_modify(&rate_limit, rate_limit.resource_limit) { + Err(GcraError::DeniedUntil { next_allowed_at }) => next_allowed_at, + _ => panic!("request #2 is only temporarily denied"), + }; + + assert!( + next_allowed_ts >= first_req_ts + rate_limit.increment_interval(1), + "we should only be allowed after the burst period {:?} >= {:?}", + next_allowed_ts, + first_req_ts + rate_limit.period + ); + assert_eq!(after_first_tat, gcra.tat, "State should be unchanged.") + } + + #[test] + fn gcra_refreshed_after_period() { + let past_time = Instant::now() - Duration::from_millis(1001); + let mut gcra = GcraState { + tat: Some(past_time), + }; + let rate_limit = RateLimit::new(1, Duration::from_secs(1)); + assert_eq!( + Ok(()), + gcra.check_and_modify(&rate_limit, 1), + "request #1 should pass" + ); + + assert!( + matches!(gcra.check_and_modify(&rate_limit, 1), Err(_allowed_at)), + "request #2 should fail" + ); + } +} diff --git a/ext/gcra-rs/src/lib.rs b/ext/gcra-rs/src/lib.rs new file mode 100644 index 000000000..8c644db99 --- /dev/null +++ b/ext/gcra-rs/src/lib.rs @@ -0,0 +1,58 @@ +//! Library which implements the core +//! [GCRA](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm) functionality in rust. +//! +//! # Features +//! - `rate-limiter` a LRU + expiring rate limiter. Implements `Send + Sync` so +//! can be used asynchronously. +//! +//! # Usage +//! +//! ```rust +//! use gcra::{GcraState, RateLimit}; +//! +//! fn check_rate_limit() { +//! const LIMIT: u32 = 1; +//! // Create a rate limit that allows `1/1s` +//! let rate_limit = RateLimit::per_sec(LIMIT); +//! +//! let mut user_state = GcraState::default(); +//! assert!(user_state.check_and_modify(&rate_limit, 1).is_ok()); +//! assert!( +//! user_state.check_and_modify(&rate_limit, 1).is_err(), +//! "We should be over the limit now" +//! ); +//! } +//! ``` +//! +//! ## With `rate-limiter` +//! +//! ```rust +//! use gcra::{GcraError, RateLimit, RateLimiter}; +//! +//! #[tokio::main] +//! async fn main() -> Result<(), GcraError> { +//! let rate_limit = RateLimit::per_sec(2); +//! let mut rl = RateLimiter::new(4); +//! +//! rl.check("key", rate_limit.clone(), 1).await?; +//! rl.check("key", rate_limit.clone(), 1).await?; +//! +//! match rl.check("key", rate_limit.clone(), 1).await { +//! Err(GcraError::DeniedUntil { next_allowed_at }) => { +//! print!("Denied: Request next at {:?}", next_allowed_at); +//! Ok(()) +//! } +//! unexpected => panic!("Opps something went wrong! {:?}", unexpected), +//! } +//! } +//! ``` + +mod gcra; +mod rate_limit; +#[cfg(feature = "rate-limiter")] +mod rate_limiter; + +pub use crate::gcra::{GcraError, GcraState}; +pub use crate::rate_limit::RateLimit; +#[cfg(feature = "rate-limiter")] +pub use crate::rate_limiter::{RateLimitEntry, RateLimitRequest, RateLimiter}; diff --git a/ext/gcra-rs/src/rate_limit.rs b/ext/gcra-rs/src/rate_limit.rs new file mode 100644 index 000000000..77189ad15 --- /dev/null +++ b/ext/gcra-rs/src/rate_limit.rs @@ -0,0 +1,45 @@ +use std::time::Duration; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +/// Defines the configuration for a GCRA rate limit. +pub struct RateLimit { + // Amount of resources that are allowed in a given period. + pub resource_limit: u32, + // The length of which to allow access to the resource. + pub period: Duration, + + /// Incremental duration cost of a single resource check + pub emission_interval: Duration, +} + +impl RateLimit { + pub fn new(resource_limit: u32, period: Duration) -> Self { + let emission_interval = period / resource_limit; + Self { + resource_limit, + period, + emission_interval, + } + } + + #[inline] + pub fn per_sec(resource_limit: u32) -> Self { + Self::new(resource_limit, Duration::from_secs(1)) + } + + /// Given a `cost`, calculates the increment interval. + pub fn increment_interval(&self, cost: u32) -> Duration { + self.emission_interval * cost + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rate_limit_emission_interal() { + let rate_limit = RateLimit::new(10, Duration::from_secs(20)); + assert_eq!(Duration::from_secs(2), rate_limit.emission_interval) + } +} diff --git a/ext/gcra-rs/src/rate_limiter/clock.rs b/ext/gcra-rs/src/rate_limiter/clock.rs new file mode 100644 index 000000000..c3491a8be --- /dev/null +++ b/ext/gcra-rs/src/rate_limiter/clock.rs @@ -0,0 +1,48 @@ +use std::time::Instant; + +/// Abstraction for getting time. +pub trait Clock { + fn now(&self) -> Instant { + Instant::now() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InstantClock; +impl Clock for InstantClock {} + +#[cfg(test)] +pub mod tests { + use super::*; + + use std::{ + sync::{Arc, Mutex}, + time::{Duration, Instant}, + }; + + #[derive(Debug, Clone)] + pub struct FakeClock { + now: Instant, + delta: Arc<Mutex<Duration>>, + } + + impl Clock for FakeClock { + fn now(&self) -> Instant { + self.now + *self.delta.lock().unwrap() + } + } + + impl FakeClock { + pub fn new() -> Self { + Self { + now: Instant::now(), + delta: Arc::new(Mutex::new(Duration::default())), + } + } + + pub fn advance_by(&self, duration: Duration) { + let mut delta = self.delta.lock().unwrap(); + *delta += duration; + } + } +} diff --git a/ext/gcra-rs/src/rate_limiter/entry.rs b/ext/gcra-rs/src/rate_limiter/entry.rs new file mode 100644 index 000000000..c10732798 --- /dev/null +++ b/ext/gcra-rs/src/rate_limiter/entry.rs @@ -0,0 +1,33 @@ +use std::{ + ops::{Deref, DerefMut}, + time::Instant, +}; + +use crate::{GcraState, RateLimit}; + +#[derive(Default, Debug, Clone)] +pub struct RateLimitEntry { + pub gcra_state: GcraState, + pub expires_at: Option<Instant>, +} + +impl Deref for RateLimitEntry { + type Target = GcraState; + + fn deref(&self) -> &Self::Target { + &self.gcra_state + } +} + +impl DerefMut for RateLimitEntry { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.gcra_state + } +} + +impl RateLimitEntry { + pub(super) fn update_expiration(&mut self, rate_limit: &RateLimit) { + let expires_at = self.tat.unwrap_or_else(Instant::now) + rate_limit.period; + self.expires_at = Some(expires_at); + } +} diff --git a/ext/gcra-rs/src/rate_limiter/mod.rs b/ext/gcra-rs/src/rate_limiter/mod.rs new file mode 100644 index 000000000..c089639f4 --- /dev/null +++ b/ext/gcra-rs/src/rate_limiter/mod.rs @@ -0,0 +1,7 @@ +mod clock; +mod entry; +#[allow(clippy::module_inception)] +mod rate_limiter; + +pub use entry::*; +pub use rate_limiter::*; diff --git a/ext/gcra-rs/src/rate_limiter/rate_limiter.rs b/ext/gcra-rs/src/rate_limiter/rate_limiter.rs new file mode 100644 index 000000000..ecbafadd5 --- /dev/null +++ b/ext/gcra-rs/src/rate_limiter/rate_limiter.rs @@ -0,0 +1,265 @@ +use dashmap::DashMap; +use rustc_hash::FxHasher; +use std::{ + fmt::Display, + hash::{BuildHasher, BuildHasherDefault, Hash}, + time::Instant, +}; + +use super::{ + clock::{Clock, InstantClock}, + entry::RateLimitEntry, +}; +use crate::{GcraError, RateLimit}; + +pub type FxBuildHasher = BuildHasherDefault<FxHasher>; + +#[derive(Clone, Hash, PartialEq, Eq, Debug)] +pub struct RateLimitRequest<T: Eq + Hash> { + key: T, +} + +impl<T> Display for RateLimitRequest<T> +where + T: Display + Eq + Hash, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "RateLimitRequest={}", self.key) + } +} + +/// A sharded rate limiter implementation using an internal [GcraState] per entry. +/// It is `Send + Sync + Clone` and manages an internal LRU with expiration. +#[derive(Clone)] +pub struct RateLimiter<T: Eq + Hash, C = InstantClock, S = FxBuildHasher> { + clock: C, + map: DashMap<RateLimitRequest<T>, RateLimitEntry, S>, +} + +impl<Key> RateLimiter<Key, InstantClock, FxBuildHasher> +where + Key: Send + Clone + Hash + Eq + Display + 'static, +{ + /// Constructs an sharded instance of a rate limiter. + pub fn new(max_data_capacity: usize) -> Self { + Self { + clock: InstantClock, + map: DashMap::with_capacity_and_hasher(max_data_capacity, FxBuildHasher::default()), + } + } + + /// Constructs an sharded instance of a rate limiter with a specific amount of shards. + pub fn with_shards(max_data_capacity: usize, num_shards: usize) -> Self { + Self { + clock: InstantClock, + map: DashMap::with_capacity_and_hasher_and_shard_amount( + max_data_capacity, + FxBuildHasher::default(), + num_shards, + ), + } + } +} + +impl<Key, C, S> RateLimiter<Key, C, S> +where + Key: Send + Clone + Hash + Eq + Display + 'static, + C: Clock, + S: Default + BuildHasher + Clone, +{ + pub fn with_clock(clock: C) -> Self { + Self { + clock, + map: DashMap::default(), + } + } + + /// Check to see if [key] is rate limited. + /// # Errors + /// - [GcraError::DeniedUntil] if the request can succeed after the [Instant] returned. + /// - [GcraError::DeniedIndefinitely] if the request can never succeed + #[inline] + pub async fn check( + &mut self, + key: Key, + rate_limit: RateLimit, + cost: u32, + ) -> Result<Instant, GcraError> { + self.check_at(key, rate_limit, cost, self.clock.now()).await + } + + /// Check to see if [key] is rate limited. + /// + /// # Errors + /// - [GcraError::DeniedUntil] if the request can succeed after the [Instant] returned. + /// - [GcraError::DeniedIndefinitely] if the request can never succeed + pub async fn check_at( + &mut self, + key: Key, + rate_limit: RateLimit, + cost: u32, + arrived_at: Instant, + ) -> Result<Instant, GcraError> { + let request_key = RateLimitRequest { key }; + + let mut entry = self.map.entry(request_key.clone()).or_default(); + match entry.check_and_modify_at(&rate_limit, arrived_at, cost) { + Ok(_) => { + entry.update_expiration(&rate_limit); + // Guaranteed to be set from update_expiration + let expires_at = entry.expires_at.unwrap(); + Ok(expires_at) + } + Err(e @ GcraError::DeniedUntil { .. }) => Err(e), + Err(e @ GcraError::DeniedIndefinitely { .. }) => { + // Free the lock so we can remove the entry + drop(entry); + // No need to keep this in the map + self.map.remove(&request_key); + Err(e) + } + } + } + + /// Removes entries that have expired + pub fn prune_expired(&mut self) { + let now = self.clock.now(); + + self.map.retain(|_key, entry| match entry.expires_at { + Some(expires_at) => expires_at > now, + None => true, + }) + } +} + +#[cfg(test)] +mod tests { + use crate::rate_limiter::clock::tests::FakeClock; + use core::panic; + use std::time::{Duration, Instant}; + + use super::*; + + #[tokio::test] + async fn rate_limiter_run_until_denied() { + let rate_limit = RateLimit::new(3, Duration::from_secs(3)); + let mut rl = RateLimiter::with_shards(4, 2); + + for _ in 0..rate_limit.resource_limit { + assert!( + rl.check("key", rate_limit.clone(), 1).await.is_ok(), + "Shouldn't be rate limited yet" + ); + } + + match rl.check("key", rate_limit, 1).await { + Ok(_) => panic!("We should be rate limited"), + Err(GcraError::DeniedUntil { next_allowed_at }) => { + assert!(next_allowed_at > Instant::now()) + } + Err(_) => panic!("Unexpected error"), + } + } + + #[tokio::test] + async fn rate_limiter_indefinitly_denied() { + let rate_limit = RateLimit::new(3, Duration::from_secs(3)); + let mut rl = RateLimiter::with_shards(4, 2); + + match rl.check("key", rate_limit.clone(), 9).await { + Ok(_) => panic!("We should be rate limited"), + Err(GcraError::DeniedIndefinitely { + cost, + rate_limit: err_rate_limit, + }) => { + assert_eq!(cost, 9); + assert_eq!(err_rate_limit, rate_limit); + } + Err(_) => panic!("Unexpected error"), + } + } + + #[tokio::test] + async fn rate_limiter_leaks() { + let rate_limit = RateLimit::per_sec(2); + let mut rl = RateLimiter::with_shards(4, 2); + + let now = Instant::now(); + assert!(rl.check_at("key", rate_limit.clone(), 1, now).await.is_ok()); + assert!( + rl.check_at( + "key", + rate_limit.clone(), + 1, + now + Duration::from_millis(250) + ) + .await + .is_ok(), + "delay the 2nd check" + ); + assert!( + rl.check_at( + "key", + rate_limit.clone(), + 1, + now + Duration::from_millis(251) + ) + .await + .is_err(), + "check we are denied start" + ); + assert!( + rl.check_at( + "key", + rate_limit.clone(), + 1, + now + Duration::from_millis(499) + ) + .await + .is_err(), + "check we are denied end" + ); + assert!( + rl.check_at( + "key", + rate_limit.clone(), + 1, + now + Duration::from_millis(501) + ) + .await + .is_ok(), + "1st use should be released" + ) + } + + #[tokio::test] + async fn rate_limiter_prune_expired() { + let clock = FakeClock::new(); + + let rate_limit = RateLimit::per_sec(3); + let mut rl: RateLimiter<_, _, FxBuildHasher> = RateLimiter::with_clock(clock.clone()); + + for index in 0..rate_limit.resource_limit { + assert!( + rl.check(index, rate_limit.clone(), 1).await.is_ok(), + "Shouldn't be rate limited yet" + ); + } + + let before_len = rl.map.len(); + rl.prune_expired(); + let after_len = rl.map.len(); + assert_eq!( + before_len, after_len, + "Nothing has expired, no elements should be removed" + ); + + clock.advance_by(Duration::from_secs(10)); + rl.prune_expired(); + let after_len = rl.map.len(); + assert_eq!( + 0, after_len, + "All entries have expired, no elements expected" + ); + } +} diff --git a/ext/libp2p-bitswap/.gitignore b/ext/libp2p-bitswap/.gitignore new file mode 100644 index 000000000..53eaa2196 --- /dev/null +++ b/ext/libp2p-bitswap/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/ext/libp2p-bitswap/Cargo.lock b/ext/libp2p-bitswap/Cargo.lock new file mode 100644 index 000000000..37c651fcb --- /dev/null +++ b/ext/libp2p-bitswap/Cargo.lock @@ -0,0 +1,3707 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.3", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +dependencies = [ + "async-lock 3.3.0", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite 2.2.0", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.1.1", + "async-executor", + "async-io 2.2.2", + "async-lock 3.3.0", + "blocking", + "futures-lite 2.2.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.2.0", + "parking", + "polling 3.3.1", + "rustix 0.38.28", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f" +dependencies = [ + "async-io 1.13.0", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.28", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.2.2", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.28", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-std-resolver" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0ed2b6671c13d2c28756c5a64e04759c1e0b5d3d7ac031f521c3561e21fbcb" +dependencies = [ + "async-std", + "async-trait", + "futures-io", + "futures-util", + "hickory-resolver", + "pin-utils", + "socket2 0.5.5", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base64" +version = "0.21.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel 2.1.1", + "async-lock 3.3.0", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite 2.2.0", + "piper", + "tracing", +] + +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cached" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4dfac631a8e77b2f327f7852bb6172771f5279c4512efe79fad6067b37be3d" +dependencies = [ + "hashbrown 0.11.2", + "once_cell", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cid" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +dependencies = [ + "core2", + "multibase", + "multihash 0.16.3", + "serde", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "cid" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3" +dependencies = [ + "core2", + "multibase", + "multihash 0.18.1", + "serde", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "data-encoding-macro" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-bounded" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e2774cc104e198ef3d3e1ff4ab40f86fa3245d6cb6a3a46174f21463cee173" +dependencies = [ + "futures-timer", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +dependencies = [ + "fastrand 2.0.1", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "futures-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd3cf68c183738046838e300353e4716c674dc5e56890de4826801a6622a28" +dependencies = [ + "futures-io", + "rustls", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hickory-proto" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091a6fbccf4860009355e3efc52ff4acf37a63489aad7435372d44ceeb6fbbcf" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "socket2 0.5.5", + "thiserror", + "tinyvec", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b8f021164e6a984c9030023544c57789c51760065cd510572fedcfb04164e8" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tracing", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if-addrs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "if-watch" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" +dependencies = [ + "async-io 2.2.2", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "smol", + "system-configuration", + "windows", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.3", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.5", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libipld" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9c3aa309c260aa2f174bac968901eddc546e9d85950c28eae6a7bec402f926" +dependencies = [ + "async-trait", + "cached", + "fnv", + "libipld-cbor", + "libipld-core 0.14.0", + "libipld-macro 0.14.0", + "log", + "multihash 0.16.3", + "parking_lot", + "thiserror", +] + +[[package]] +name = "libipld" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ccd6b8ffb3afee7081fcaec00e1b099fd1c7ccf35ba5729d88538fcc3b4599" +dependencies = [ + "fnv", + "libipld-core 0.16.0", + "libipld-macro 0.16.0", + "log", + "multihash 0.18.1", + "thiserror", +] + +[[package]] +name = "libipld-cbor" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd1ab68c9d26f20c7d0dfea6eecbae8c00359875210001b33ca27d4a02f3d09" +dependencies = [ + "byteorder", + "libipld-core 0.14.0", + "thiserror", +] + +[[package]] +name = "libipld-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44790246ec6b7314cba745992c23d479d018073e66d49ae40ae1b64e5dd8eb5" +dependencies = [ + "anyhow", + "cid 0.8.6", + "core2", + "multibase", + "multihash 0.16.3", + "thiserror", +] + +[[package]] +name = "libipld-core" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acd707e8d8b092e967b2af978ed84709eaded82b75effe6cb6f6cc797ef8158" +dependencies = [ + "anyhow", + "cid 0.10.1", + "core2", + "multibase", + "multihash 0.18.1", + "thiserror", +] + +[[package]] +name = "libipld-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852c011562ae5059b67c3a917f9f5945af5a68df8e39ede4444fff33274d25e2" +dependencies = [ + "libipld-core 0.14.0", +] + +[[package]] +name = "libipld-macro" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71171c54214f866ae6722f3027f81dff0931e600e5a61e6b1b6a49ca0b5ed4ae" +dependencies = [ + "libipld-core 0.16.0", +] + +[[package]] +name = "libp2p" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681fb3f183edfbedd7a57d32ebe5dcdc0b9f94061185acf3c30249349cc6fc99" +dependencies = [ + "bytes", + "either", + "futures", + "futures-timer", + "getrandom", + "instant", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core", + "libp2p-dns", + "libp2p-identity", + "libp2p-mdns", + "libp2p-noise", + "libp2p-quic", + "libp2p-request-response", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-yamux", + "multiaddr", + "pin-project", + "rw-stream-sink", + "thiserror", +] + +[[package]] +name = "libp2p-allow-block-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "107b238b794cb83ab53b74ad5dcf7cca3200899b72fe662840cfb52f5b0a32e6" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-bitswap" +version = "0.25.1" +dependencies = [ + "async-std", + "async-trait", + "env_logger", + "fnv", + "futures", + "lazy_static", + "libipld 0.14.0", + "libipld 0.16.0", + "libp2p", + "multihash 0.16.3", + "prometheus", + "prost", + "prost-build", + "thiserror", + "tracing", + "tracing-subscriber", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cd50a78ccfada14de94cbacd3ce4b0138157f376870f13d3a8422cd075b4fd" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-core" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8130a8269e65a2554d55131c770bdf4bcd94d2b8d4efb24ca23699be65066c05" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "multiaddr", + "multihash 0.19.1", + "multistream-select", + "once_cell", + "parking_lot", + "pin-project", + "quick-protobuf", + "rand", + "rw-stream-sink", + "smallvec", + "thiserror", + "tracing", + "unsigned-varint 0.8.0", + "void", +] + +[[package]] +name = "libp2p-dns" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17cbcf7160ff35c3e8e560de4a068fe9d6cb777ea72840e48eb76ff9576c4b6" +dependencies = [ + "async-std-resolver", + "async-trait", + "futures", + "hickory-resolver", + "libp2p-core", + "libp2p-identity", + "parking_lot", + "smallvec", + "tracing", +] + +[[package]] +name = "libp2p-identity" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "hkdf", + "multihash 0.19.1", + "quick-protobuf", + "rand", + "ring 0.17.7", + "sha2", + "thiserror", + "tracing", + "zeroize", +] + +[[package]] +name = "libp2p-mdns" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49007d9a339b3e1d7eeebc4d67c05dbf23d300b7d091193ec2d3f26802d7faf2" +dependencies = [ + "async-io 2.2.2", + "async-std", + "data-encoding", + "futures", + "hickory-proto", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "rand", + "smallvec", + "socket2 0.5.5", + "tracing", + "void", +] + +[[package]] +name = "libp2p-noise" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecd0545ce077f6ea5434bcb76e8d0fe942693b4380aaad0d34a358c2bd05793" +dependencies = [ + "asynchronous-codec", + "bytes", + "curve25519-dalek", + "futures", + "libp2p-core", + "libp2p-identity", + "multiaddr", + "multihash 0.19.1", + "once_cell", + "quick-protobuf", + "rand", + "sha2", + "snow", + "static_assertions", + "thiserror", + "tracing", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "libp2p-quic" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0375cdfee57b47b313ef1f0fdb625b78aed770d33a40cf1c294a371ff5e6666" +dependencies = [ + "async-std", + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-tls", + "parking_lot", + "quinn", + "rand", + "ring 0.16.20", + "rustls", + "socket2 0.5.5", + "thiserror", + "tracing", +] + +[[package]] +name = "libp2p-request-response" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12823250fe0c45bdddea6eefa2be9a609aff1283ff4e1d8a294fdbb89572f6f" +dependencies = [ + "async-trait", + "futures", + "futures-bounded", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "rand", + "smallvec", + "tracing", + "void", +] + +[[package]] +name = "libp2p-swarm" +version = "0.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e92532fc3c4fb292ae30c371815c9b10103718777726ea5497abc268a4761866" +dependencies = [ + "async-std", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "multistream-select", + "once_cell", + "rand", + "smallvec", + "tracing", + "void", +] + +[[package]] +name = "libp2p-tcp" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2460fc2748919adff99ecbc1aab296e4579e41f374fb164149bd2c9e529d4c" +dependencies = [ + "async-io 1.13.0", + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core", + "libp2p-identity", + "socket2 0.5.5", + "tracing", +] + +[[package]] +name = "libp2p-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ce7e3c2e7569d685d08ec795157981722ff96e9e9f9eae75df3c29d02b07a5" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core", + "libp2p-identity", + "rcgen", + "ring 0.16.20", + "rustls", + "rustls-webpki", + "thiserror", + "x509-parser", + "yasna", +] + +[[package]] +name = "libp2p-yamux" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200cbe50349a44760927d50b431d77bed79b9c0a3959de1af8d24a63434b71e5" +dependencies = [ + "either", + "futures", + "libp2p-core", + "thiserror", + "tracing", + "yamux 0.12.1", + "yamux 0.13.1", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "multiaddr" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b852bc02a2da5feed68cd14fa50d0774b92790a5bdbfa932a813926c8472070" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash 0.19.1", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint 0.7.2", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "blake3", + "core2", + "digest", + "multihash-derive", + "sha2", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "multihash" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd8a792c1694c6da4f68db0a9d707c72bd260994da179e6030a5dcee00bb815" +dependencies = [ + "core2", + "multihash-derive", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "multihash" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +dependencies = [ + "core2", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multistream-select" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0df8e5eec2298a62b326ee4f0d7fe1a6b90a09dfcf9df37b38f947a8c42f19" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint 0.7.2", +] + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "async-io 1.13.0", + "bytes", + "futures", + "libc", + "log", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.3", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pem" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.28", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[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.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "protobuf", + "thiserror", +] + +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" +dependencies = [ + "bytes", + "heck 0.3.3", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quinn" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +dependencies = [ + "async-io 1.13.0", + "async-std", + "bytes", + "futures-io", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" +dependencies = [ + "bytes", + "rand", + "ring 0.16.20", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +dependencies = [ + "bytes", + "libc", + "socket2 0.5.5", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rcgen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "async-global-executor", + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.7", + "untrusted 0.9.0", +] + +[[package]] +name = "rw-stream-sink" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c9026ff5d2f23da5e45bbc283f156383001bfb09c4e44256d02c1a685fe9a1" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.7", + "untrusted 0.9.0", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "smol" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" +dependencies = [ + "async-channel 1.9.0", + "async-executor", + "async-fs", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-net", + "async-process", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "snow" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58021967fd0a5eeeb23b08df6cc244a4d4a5b4aec1d27c9e02fad1a58b4cd74e" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek", + "rand_core", + "ring 0.17.7", + "rustc_version", + "sha2", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand 2.0.1", + "redox_syscall", + "rustix 0.38.28", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "pin-project-lite", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +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-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" +dependencies = [ + "futures-io", + "futures-util", +] + +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.28", +] + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "yamux" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed0164ae619f2dc144909a9f082187ebb5893693d8c0196e8085283ccd4b776" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot", + "pin-project", + "rand", + "static_assertions", +] + +[[package]] +name = "yamux" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1d0148b89300047e72994bee99ecdabd15a9166a7b70c8b8c37c314dcc9002" +dependencies = [ + "futures", + "instant", + "log", + "nohash-hasher", + "parking_lot", + "pin-project", + "rand", + "static_assertions", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/ext/libp2p-bitswap/Cargo.toml b/ext/libp2p-bitswap/Cargo.toml new file mode 100644 index 000000000..bfad697c6 --- /dev/null +++ b/ext/libp2p-bitswap/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "libp2p-bitswap" +version = "0.25.1" +authors = ["David Craven <david@craven.ch>"] +edition = "2018" +description = "Implementation of the ipfs bitswap protocol." +license = "MIT OR Apache-2.0" +repository = "https://github.com/ipfs-rust/libp2p-bitswap" + +[features] +compat = ["prost", "prost-build"] + +[build-dependencies] +prost-build = { version = "0.9.0", optional = true } + +[dependencies] +async-trait = "0.1.52" +fnv = "1.0.7" +futures = "0.3.19" +lazy_static = "1.4.0" +libipld = { version = "0.16.0", default-features = false } +libp2p = { version = "0.53", features = ["request-response"] } +prometheus = "0.13.0" +prost = { version = "0.9.0", optional = true } +thiserror = "1.0.30" +tracing = "0.1.29" +unsigned-varint = { version = "0.7.1", features = ["futures", "std"] } + +[dev-dependencies] +async-std = { version = "1.10.0", features = ["attributes"] } +env_logger = "0.9.0" +libipld = { version = "0.14.0", default-features = false, features = ["dag-cbor"] } +libp2p = { version = "0.53.0", features = ["tcp", "noise", "yamux", "rsa", "async-std"] } +multihash = { version = "0.16.0", default-features = false, features = ["blake3", "sha2"] } +tracing-subscriber = { version = "0.3.5", features = ["env-filter", "tracing-log"] } diff --git a/ext/libp2p-bitswap/README.md b/ext/libp2p-bitswap/README.md new file mode 100644 index 000000000..3ad2dd613 --- /dev/null +++ b/ext/libp2p-bitswap/README.md @@ -0,0 +1,98 @@ +[](https://crates.io/crates/libp2p-bitswap) +[](https://docs.rs/libp2p-bitswap) + +# libp2p-bitswap + +Implementation of the bitswap protocol. + +## Efficiently syncing dags of blocks + +Bitswap is a very simple protocol. It was adapted and simplified for ipfs-embed. The message +format can be represented by the following enums. + +```rust +pub enum BitswapRequest { + Have(Cid), + Block(Cid), +} + +pub enum BitswapResponse { + Have(bool), + Block(Vec<u8>), +} +``` + +The mechanism for locating providers can be abstracted. A dht can be plugged in or a centralized +db query. The bitswap api looks as follows: + +```rust +#[derive(Debug)] +pub enum BitswapEvent { + /// Received a block from a peer. Includes the number of known missing blocks for a + /// sync query. When a block is received and missing blocks is not empty the counter + /// is increased. If missing blocks is empty the counter is decremented. + Progress(QueryId, usize), + /// A get or sync query completed. + Complete(QueryId, Result<()>), +} + +pub trait BitswapStore: Send + Sync + 'static { + /// The store params. + type Params: StoreParams; + /// A have query needs to know if the block store contains the block. + fn contains(&mut self, cid: &Cid) -> Result<bool>; + /// A block query needs to retrieve the block from the store. + fn get(&mut self, cid: &Cid) -> Result<Option<Vec<u8>>>; + /// A block response needs to insert the block into the store. + fn insert(&mut self, block: &Block<Self::Params>) -> Result<()>; + /// A sync query needs a list of missing blocks to make progress. + fn missing_blocks(&mut self, cid: &Cid) -> Result<Vec<Cid>>; +} + +pub struct BitswapConfig { + /// Timeout of a request. + pub request_timeout: Duration, + /// Time a connection is kept alive. + pub connection_keep_alive: Duration, +} + +impl<P: StoreParams> Bitswap<P> { + /// Creates a new `Bitswap` behaviour. + pub fn new(config: BitswapConfig) -> Self; + + /// Adds an address for a peer. + pub fn add_address(&mut self, peer_id: &PeerId, addr: Multiaddr); + + /// Removes an address for a peer. + pub fn remove_address(&mut self, peer_id: &PeerId, addr: &Multiaddr); + + /// Starts a get query with an initial guess of providers. + pub fn get(&mut self, cid: Cid, peers: impl Iterator<Item = PeerId>) -> QueryId; + + /// Starts a sync query with an the initial set of missing blocks. + pub fn sync(&mut self, cid: Cid, peers: Vec<PeerId>, missing: impl Iterator<Item = Cid>) -> QueryId; + + /// Cancels an in progress query. Returns true if a query was cancelled. + pub fn cancel(&mut self, id: QueryId) -> bool; + + /// Register bitswap stats in a prometheus registry. + pub fn register_metrics(&self, registry: &Registry) -> Result<()>; +} +``` + +So what happens when you create a get request? First all the providers in the initial set +are queried with the have request. As an optimization, in every batch of queries a block +request is sent instead. If the get query finds a block it returns a query complete. If the +block wasn't found in the initial set, a `Providers` event is emitted. This is where +the bitswap consumer tries to locate providers by for example performing a dht lookup. After +the locating of providers completes, it is signaled by calling `inject_providers`. The query +manager then performs bitswap requests using the new provider set which results in the block +being found or a `BlockNotFound` error. + +Often we want to sync an entire dag of blocks. We can efficiently sync dags of blocks by adding +a sync query that runs get queries in parallel for all the references of a block. The set of +providers that had a block is used as the initial set in a reference query. + +## License + +MIT OR Apache-2.0 diff --git a/ext/libp2p-bitswap/build.rs b/ext/libp2p-bitswap/build.rs new file mode 100644 index 000000000..6b7fa7b80 --- /dev/null +++ b/ext/libp2p-bitswap/build.rs @@ -0,0 +1,4 @@ +fn main() { + #[cfg(feature = "compat")] + prost_build::compile_protos(&["src/compat/bitswap_pb.proto"], &["src/compat"]).unwrap(); +} diff --git a/ext/libp2p-bitswap/src/behaviour.rs b/ext/libp2p-bitswap/src/behaviour.rs new file mode 100644 index 000000000..a195b4e08 --- /dev/null +++ b/ext/libp2p-bitswap/src/behaviour.rs @@ -0,0 +1,963 @@ +//! Handles the `/ipfs/bitswap/1.0.0` and `/ipfs/bitswap/1.1.0` protocols. This +//! allows exchanging IPFS blocks. +//! +//! # Usage +//! +//! The `Bitswap` struct implements the `NetworkBehaviour` trait. When used, it +//! will allow providing and reciving IPFS blocks. +#[cfg(feature = "compat")] +use crate::compat::{CompatMessage, CompatProtocol, InboundMessage}; +use crate::protocol::{ + BitswapCodec, BitswapProtocol, BitswapRequest, BitswapResponse, RequestType, +}; +use crate::query::{QueryEvent, QueryId, QueryManager, Request, Response}; +use crate::stats::*; +use fnv::FnvHashMap; +#[cfg(feature = "compat")] +use fnv::FnvHashSet; +use futures::{ + channel::mpsc, + stream::{Stream, StreamExt}, + task::{Context, Poll}, +}; +use libipld::{error::BlockNotFound, store::StoreParams, Block, Cid, Result}; +#[cfg(feature = "compat")] +use libp2p::core::either::EitherOutput; +use libp2p::core::{Endpoint, Multiaddr}; +use libp2p::request_response::OutboundRequestId; +use libp2p::swarm::derive_prelude::{ConnectionClosed, DialFailure, FromSwarm, ListenFailure}; +use libp2p::swarm::{ + ConnectionDenied, ConnectionId, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, +}; +#[cfg(feature = "compat")] +use libp2p::swarm::{ConnectionHandlerSelect, NotifyHandler, OneShotHandler}; +use libp2p::PeerId; +use libp2p::{ + request_response::{ + self, InboundFailure, InboundRequestId, OutboundFailure, ProtocolSupport, ResponseChannel, + }, + swarm::NetworkBehaviour, +}; +use prometheus::Registry; +use std::{pin::Pin, time::Duration}; + +/// Bitswap response channel. +pub type Channel = ResponseChannel<BitswapResponse>; + +/// Event emitted by the bitswap behaviour. +#[derive(Debug)] +pub enum BitswapEvent { + /// Received a block from a peer. Includes the number of known missing blocks for a + /// sync query. When a block is received and missing blocks is not empty the counter + /// is increased. If missing blocks is empty the counter is decremented. + Progress(QueryId, usize), + /// A get or sync query completed. + Complete(QueryId, Result<()>), +} + +/// Trait implemented by a block store. +pub trait BitswapStore: Send + Sync + 'static { + /// The store params. + type Params: StoreParams; + /// A have query needs to know if the block store contains the block. + fn contains(&mut self, cid: &Cid) -> Result<bool>; + /// A block query needs to retrieve the block from the store. + fn get(&mut self, cid: &Cid) -> Result<Option<Vec<u8>>>; + /// A block response needs to insert the block into the store. + fn insert(&mut self, block: &Block<Self::Params>) -> Result<()>; + /// A sync query needs a list of missing blocks to make progress. + fn missing_blocks(&mut self, cid: &Cid) -> Result<Vec<Cid>>; +} + +/// Bitswap configuration. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct BitswapConfig { + /// Timeout of a request. + pub request_timeout: Duration, +} + +impl BitswapConfig { + /// Creates a new `BitswapConfig`. + pub fn new() -> Self { + Self { + request_timeout: Duration::from_secs(10), + } + } +} + +impl Default for BitswapConfig { + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +enum BitswapId { + Bitswap(OutboundRequestId), + #[cfg(feature = "compat")] + Compat(Cid), +} + +enum BitswapChannel { + Bitswap(Channel), + #[cfg(feature = "compat")] + Compat(PeerId, Cid), +} + +/// Network behaviour that handles sending and receiving blocks. +pub struct Bitswap<P: StoreParams> { + /// Inner behaviour. + inner: request_response::Behaviour<BitswapCodec<P>>, + /// Query manager. + query_manager: QueryManager, + /// Requests. + requests: FnvHashMap<BitswapId, QueryId>, + /// Db request channel. + db_tx: mpsc::UnboundedSender<DbRequest<P>>, + /// Db response channel. + db_rx: mpsc::UnboundedReceiver<DbResponse>, + /// Compat peers. + #[cfg(feature = "compat")] + compat: FnvHashSet<PeerId>, +} + +impl<P: StoreParams> Bitswap<P> { + /// Creates a new `Bitswap` behaviour. + pub fn new<S: BitswapStore<Params = P>>(config: BitswapConfig, store: S) -> Self { + let rr_config = + request_response::Config::default().with_request_timeout(config.request_timeout); + let protocols = std::iter::once((BitswapProtocol, ProtocolSupport::Full)); + let inner = request_response::Behaviour::with_codec( + BitswapCodec::<P>::default(), + protocols, + rr_config, + ); + let (db_tx, db_rx) = start_db_thread(store); + Self { + inner, + query_manager: Default::default(), + requests: Default::default(), + db_tx, + db_rx, + #[cfg(feature = "compat")] + compat: Default::default(), + } + } + + /// Adds an address for a peer. + pub fn add_address(&mut self, peer_id: &PeerId, addr: Multiaddr) { + #[allow(deprecated)] + self.inner.add_address(peer_id, addr); + } + + /// Removes an address for a peer. + pub fn remove_address(&mut self, peer_id: &PeerId, addr: &Multiaddr) { + #[allow(deprecated)] + self.inner.remove_address(peer_id, addr); + } + + /// Starts a get query with an initial guess of providers. + pub fn get(&mut self, cid: Cid, peers: impl Iterator<Item = PeerId>) -> QueryId { + self.query_manager.get(None, cid, peers) + } + + /// Starts a sync query with an the initial set of missing blocks. + pub fn sync( + &mut self, + cid: Cid, + peers: Vec<PeerId>, + missing: impl Iterator<Item = Cid>, + ) -> QueryId { + self.query_manager.sync(cid, peers, missing) + } + + /// Cancels an in progress query. Returns true if a query was cancelled. + pub fn cancel(&mut self, id: QueryId) -> bool { + let res = self.query_manager.cancel(id); + if res { + REQUESTS_CANCELED.inc(); + } + res + } + + /// Registers prometheus metrics. + pub fn register_metrics(&self, registry: &Registry) -> Result<()> { + registry.register(Box::new(REQUESTS_TOTAL.clone()))?; + registry.register(Box::new(REQUEST_DURATION_SECONDS.clone()))?; + registry.register(Box::new(REQUESTS_CANCELED.clone()))?; + registry.register(Box::new(BLOCK_NOT_FOUND.clone()))?; + registry.register(Box::new(PROVIDERS_TOTAL.clone()))?; + registry.register(Box::new(MISSING_BLOCKS_TOTAL.clone()))?; + registry.register(Box::new(RECEIVED_BLOCK_BYTES.clone()))?; + registry.register(Box::new(RECEIVED_INVALID_BLOCK_BYTES.clone()))?; + registry.register(Box::new(SENT_BLOCK_BYTES.clone()))?; + registry.register(Box::new(RESPONSES_TOTAL.clone()))?; + registry.register(Box::new(THROTTLED_INBOUND.clone()))?; + registry.register(Box::new(THROTTLED_OUTBOUND.clone()))?; + registry.register(Box::new(OUTBOUND_FAILURE.clone()))?; + registry.register(Box::new(INBOUND_FAILURE.clone()))?; + Ok(()) + } +} + +enum DbRequest<P: StoreParams> { + Bitswap(BitswapChannel, BitswapRequest), + Insert(Block<P>), + MissingBlocks(QueryId, Cid), +} + +enum DbResponse { + Bitswap(BitswapChannel, BitswapResponse), + MissingBlocks(QueryId, Result<Vec<Cid>>), +} + +fn start_db_thread<S: BitswapStore>( + mut store: S, +) -> ( + mpsc::UnboundedSender<DbRequest<S::Params>>, + mpsc::UnboundedReceiver<DbResponse>, +) { + let (tx, requests) = mpsc::unbounded(); + let (responses, rx) = mpsc::unbounded(); + std::thread::spawn(move || { + let mut requests: mpsc::UnboundedReceiver<DbRequest<S::Params>> = requests; + while let Some(request) = futures::executor::block_on(requests.next()) { + match request { + DbRequest::Bitswap(channel, request) => { + let response = match request.ty { + RequestType::Have => { + let have = store.contains(&request.cid).ok().unwrap_or_default(); + if have { + RESPONSES_TOTAL.with_label_values(&["have"]).inc(); + } else { + RESPONSES_TOTAL.with_label_values(&["dont_have"]).inc(); + } + tracing::trace!("have {}", have); + BitswapResponse::Have(have) + } + RequestType::Block => { + let block = store.get(&request.cid).ok().unwrap_or_default(); + if let Some(data) = block { + RESPONSES_TOTAL.with_label_values(&["block"]).inc(); + SENT_BLOCK_BYTES.inc_by(data.len() as u64); + tracing::trace!("block {}", data.len()); + BitswapResponse::Block(data) + } else { + RESPONSES_TOTAL.with_label_values(&["dont_have"]).inc(); + tracing::trace!("have false"); + BitswapResponse::Have(false) + } + } + }; + responses + .unbounded_send(DbResponse::Bitswap(channel, response)) + .ok(); + } + DbRequest::Insert(block) => { + if let Err(err) = store.insert(&block) { + tracing::error!("error inserting blocks {}", err); + } + } + DbRequest::MissingBlocks(id, cid) => { + let res = store.missing_blocks(&cid); + responses + .unbounded_send(DbResponse::MissingBlocks(id, res)) + .ok(); + } + } + } + }); + (tx, rx) +} + +impl<P: StoreParams> Bitswap<P> { + /// Processes an incoming bitswap request. + fn inject_request(&mut self, channel: BitswapChannel, request: BitswapRequest) { + self.db_tx + .unbounded_send(DbRequest::Bitswap(channel, request)) + .ok(); + } + + /// Processes an incoming bitswap response. + fn inject_response(&mut self, id: BitswapId, peer: PeerId, response: BitswapResponse) { + if let Some(id) = self.requests.remove(&id) { + match response { + BitswapResponse::Have(have) => { + self.query_manager + .inject_response(id, Response::Have(peer, have)); + } + BitswapResponse::Block(data) => { + if let Some(info) = self.query_manager.query_info(id) { + let len = data.len(); + if let Ok(block) = Block::new(info.cid, data) { + RECEIVED_BLOCK_BYTES.inc_by(len as u64); + self.db_tx.unbounded_send(DbRequest::Insert(block)).ok(); + self.query_manager + .inject_response(id, Response::Block(peer, true)); + } else { + tracing::error!("received invalid block"); + RECEIVED_INVALID_BLOCK_BYTES.inc_by(len as u64); + self.query_manager + .inject_response(id, Response::Block(peer, false)); + } + } + } + } + } + } + + fn inject_outbound_failure( + &mut self, + peer: &PeerId, + request_id: OutboundRequestId, + error: &OutboundFailure, + ) { + tracing::debug!( + "bitswap outbound failure {} {} {:?}", + peer, + request_id, + error + ); + match error { + OutboundFailure::DialFailure => { + OUTBOUND_FAILURE.with_label_values(&["dial_failure"]).inc(); + } + OutboundFailure::Timeout => { + OUTBOUND_FAILURE.with_label_values(&["timeout"]).inc(); + } + OutboundFailure::ConnectionClosed => { + OUTBOUND_FAILURE + .with_label_values(&["connection_closed"]) + .inc(); + } + OutboundFailure::UnsupportedProtocols => { + OUTBOUND_FAILURE + .with_label_values(&["unsupported_protocols"]) + .inc(); + } + OutboundFailure::Io(_) => { + INBOUND_FAILURE.with_label_values(&["io_error"]).inc(); + } + } + } + + fn inject_inbound_failure( + &mut self, + peer: &PeerId, + request_id: InboundRequestId, + error: &InboundFailure, + ) { + tracing::error!( + "bitswap inbound failure {} {} {:?}", + peer, + request_id, + error + ); + match error { + InboundFailure::Timeout => { + INBOUND_FAILURE.with_label_values(&["timeout"]).inc(); + } + InboundFailure::ConnectionClosed => { + INBOUND_FAILURE + .with_label_values(&["connection_closed"]) + .inc(); + } + InboundFailure::UnsupportedProtocols => { + INBOUND_FAILURE + .with_label_values(&["unsupported_protocols"]) + .inc(); + } + InboundFailure::ResponseOmission => { + INBOUND_FAILURE + .with_label_values(&["response_omission"]) + .inc(); + } + InboundFailure::Io(_) => { + INBOUND_FAILURE.with_label_values(&["io_error"]).inc(); + } + } + } +} + +impl<P: StoreParams> NetworkBehaviour for Bitswap<P> { + #[cfg(not(feature = "compat"))] + type ConnectionHandler = + <request_response::Behaviour<BitswapCodec<P>> as NetworkBehaviour>::ConnectionHandler; + + #[cfg(feature = "compat")] + #[allow(clippy::type_complexity)] + type ConnectionHandler = ConnectionHandlerSelect< + <RequestResponse<BitswapCodec<P>> as NetworkBehaviour>::ConnectionHandler, + OneShotHandler<CompatProtocol, CompatMessage, InboundMessage>, + >; + type ToSwarm = BitswapEvent; + + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + self.inner + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<THandler<Self>, ConnectionDenied> { + self.inner.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + ) + } + + fn handle_pending_outbound_connection( + &mut self, + connection_id: ConnectionId, + maybe_peer: Option<PeerId>, + addresses: &[Multiaddr], + effective_role: Endpoint, + ) -> Result<Vec<Multiaddr>, ConnectionDenied> { + self.inner.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + ) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result<THandler<Self>, ConnectionDenied> { + self.inner + .handle_established_outbound_connection(connection_id, peer, addr, role_override) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + match event { + FromSwarm::ConnectionEstablished(ev) => self + .inner + .on_swarm_event(FromSwarm::ConnectionEstablished(ev)), + FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + endpoint, + remaining_established, + }) => { + #[cfg(feature = "compat")] + if remaining_established == 0 { + self.compat.remove(&peer_id); + } + #[cfg(feature = "compat")] + let (handler, _oneshot) = handler.into_inner(); + self.inner + .on_swarm_event(FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + endpoint, + remaining_established, + })); + } + FromSwarm::DialFailure(DialFailure { + peer_id, + connection_id, + error, + }) => { + #[cfg(feature = "compat")] + let (handler, _oneshot) = handler.into_inner(); + self.inner + .on_swarm_event(FromSwarm::DialFailure(DialFailure { + peer_id, + connection_id, + error, + })); + } + FromSwarm::AddressChange(ev) => self.inner.on_swarm_event(FromSwarm::AddressChange(ev)), + FromSwarm::ListenFailure(ListenFailure { + local_addr, + send_back_addr, + error, + connection_id, + }) => { + #[cfg(feature = "compat")] + let (handler, _oneshot) = handler.into_inner(); + self.inner + .on_swarm_event(FromSwarm::ListenFailure(ListenFailure { + local_addr, + send_back_addr, + error, + connection_id, + })); + } + FromSwarm::NewListener(ev) => self.inner.on_swarm_event(FromSwarm::NewListener(ev)), + FromSwarm::NewListenAddr(ev) => self.inner.on_swarm_event(FromSwarm::NewListenAddr(ev)), + FromSwarm::ExpiredListenAddr(ev) => { + self.inner.on_swarm_event(FromSwarm::ExpiredListenAddr(ev)) + } + FromSwarm::ListenerError(ev) => self.inner.on_swarm_event(FromSwarm::ListenerError(ev)), + FromSwarm::ListenerClosed(ev) => { + self.inner.on_swarm_event(FromSwarm::ListenerClosed(ev)) + } + _ => {} + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + conn: ConnectionId, + event: THandlerOutEvent<Self>, + ) { + #[cfg(not(feature = "compat"))] + return self.inner.on_connection_handler_event(peer_id, conn, event); + #[cfg(feature = "compat")] + match event { + EitherOutput::First(event) => { + self.inner.on_connection_handler_event(peer_id, conn, event) + } + EitherOutput::Second(msg) => { + for msg in msg.0 { + match msg { + CompatMessage::Request(req) => { + tracing::trace!("received compat request"); + self.inject_request(BitswapChannel::Compat(peer_id, req.cid), req); + } + CompatMessage::Response(cid, res) => { + tracing::trace!("received compat response"); + self.inject_response(BitswapId::Compat(cid), peer_id, res); + } + } + } + } + } + } + + fn poll(&mut self, cx: &mut Context) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> { + let mut exit = false; + while !exit { + exit = true; + while let Poll::Ready(Some(response)) = Pin::new(&mut self.db_rx).poll_next(cx) { + exit = false; + match response { + DbResponse::Bitswap(channel, response) => match channel { + BitswapChannel::Bitswap(channel) => { + self.inner.send_response(channel, response).ok(); + } + #[cfg(feature = "compat")] + BitswapChannel::Compat(peer_id, cid) => { + let compat = CompatMessage::Response(cid, response); + return Poll::Ready(FromSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: EitherOutput::Second(compat), + }); + } + }, + DbResponse::MissingBlocks(id, res) => match res { + Ok(missing) => { + MISSING_BLOCKS_TOTAL.inc_by(missing.len() as u64); + self.query_manager + .inject_response(id, Response::MissingBlocks(missing)); + } + Err(err) => { + self.query_manager.cancel(id); + let event = BitswapEvent::Complete(id, Err(err)); + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } + }, + } + } + while let Some(query) = self.query_manager.next() { + exit = false; + match query { + QueryEvent::Request(id, req) => match req { + Request::Have(peer_id, cid) => { + let req = BitswapRequest { + ty: RequestType::Have, + cid, + }; + let rid = self.inner.send_request(&peer_id, req); + self.requests.insert(BitswapId::Bitswap(rid), id); + } + Request::Block(peer_id, cid) => { + let req = BitswapRequest { + ty: RequestType::Block, + cid, + }; + let rid = self.inner.send_request(&peer_id, req); + self.requests.insert(BitswapId::Bitswap(rid), id); + } + Request::MissingBlocks(cid) => { + self.db_tx + .unbounded_send(DbRequest::MissingBlocks(id, cid)) + .ok(); + } + }, + QueryEvent::Progress(id, missing) => { + let event = BitswapEvent::Progress(id, missing); + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } + QueryEvent::Complete(id, res) => { + if res.is_err() { + BLOCK_NOT_FOUND.inc(); + } + let event = BitswapEvent::Complete( + id, + res.map_err(|cid| BlockNotFound(cid).into()), + ); + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } + } + } + while let Poll::Ready(event) = self.inner.poll(cx) { + exit = false; + + let event = match event { + ToSwarm::GenerateEvent(event) => event, + other => return Poll::Ready(other.map_out(|_| unreachable!())), + }; + + match event { + request_response::Event::Message { peer, message } => match message { + request_response::Message::Request { + request_id: _, + request, + channel, + } => self.inject_request(BitswapChannel::Bitswap(channel), request), + request_response::Message::Response { + request_id, + response, + } => self.inject_response(BitswapId::Bitswap(request_id), peer, response), + }, + request_response::Event::ResponseSent { .. } => {} + request_response::Event::OutboundFailure { + peer, + request_id, + error, + } => { + self.inject_outbound_failure(&peer, request_id, &error); + #[cfg(feature = "compat")] + if let OutboundFailure::UnsupportedProtocols = error { + if let Some(id) = self.requests.remove(&BitswapId::Bitswap(request_id)) + { + if let Some(info) = self.query_manager.query_info(id) { + let ty = match info.label { + "have" => RequestType::Have, + "block" => RequestType::Block, + _ => unreachable!(), + }; + let request = BitswapRequest { ty, cid: info.cid }; + self.requests.insert(BitswapId::Compat(info.cid), id); + tracing::trace!("adding compat peer {}", peer); + self.compat.insert(peer); + return Poll::Ready(FromSwarm::NotifyHandler { + peer_id: peer, + handler: NotifyHandler::Any, + event: EitherOutput::Second(CompatMessage::Request( + request, + )), + }); + } + } + } + if let Some(id) = self.requests.remove(&BitswapId::Bitswap(request_id)) { + self.query_manager + .inject_response(id, Response::Have(peer, false)); + } + } + request_response::Event::InboundFailure { + peer, + request_id, + error, + } => { + self.inject_inbound_failure(&peer, request_id, &error); + } + } + } + } + Poll::Pending + } +} + +#[cfg(test)] +mod tests { + use super::*; + use async_std::task; + use futures::prelude::*; + use libipld::block::Block; + use libipld::cbor::DagCborCodec; + use libipld::ipld; + use libipld::ipld::Ipld; + use libipld::multihash::Code; + use libipld::store::DefaultParams; + use libp2p::identity; + use libp2p::swarm::SwarmEvent; + use libp2p::tcp::{self}; + use libp2p::{noise, yamux, PeerId, Swarm}; + use std::sync::{Arc, Mutex}; + use std::time::Duration; + + fn tracing_try_init() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .ok(); + } + + fn create_block(ipld: Ipld) -> Block<DefaultParams> { + Block::encode(DagCborCodec, Code::Blake3_256, &ipld).unwrap() + } + + #[derive(Clone, Default)] + struct Store(Arc<Mutex<FnvHashMap<Cid, Vec<u8>>>>); + + impl BitswapStore for Store { + type Params = DefaultParams; + fn contains(&mut self, cid: &Cid) -> Result<bool> { + Ok(self.0.lock().unwrap().contains_key(cid)) + } + fn get(&mut self, cid: &Cid) -> Result<Option<Vec<u8>>> { + Ok(self.0.lock().unwrap().get(cid).cloned()) + } + fn insert(&mut self, block: &Block<Self::Params>) -> Result<()> { + self.0 + .lock() + .unwrap() + .insert(*block.cid(), block.data().to_vec()); + Ok(()) + } + fn missing_blocks(&mut self, cid: &Cid) -> Result<Vec<Cid>> { + let mut stack = vec![*cid]; + let mut missing = vec![]; + while let Some(cid) = stack.pop() { + if let Some(data) = self.get(&cid)? { + let block = Block::<Self::Params>::new_unchecked(cid, data); + block.references(&mut stack)?; + } else { + missing.push(cid); + } + } + Ok(missing) + } + } + + struct Peer { + peer_id: PeerId, + addr: Multiaddr, + store: Store, + swarm: Swarm<Bitswap<DefaultParams>>, + } + + impl Peer { + fn new() -> Self { + // Create a public/private key pair, either random or based on a seed. + let id_keys = identity::Keypair::generate_ed25519(); + let peer_id = id_keys.public().to_peer_id(); + let store = Store::default(); + + let mut swarm = libp2p::SwarmBuilder::with_existing_identity(id_keys) + .with_async_std() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + ) + .unwrap() + .with_behaviour(|_| Bitswap::new(BitswapConfig::new(), store.clone())) + .unwrap() + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) + .build(); + + Swarm::listen_on(&mut swarm, "/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap(); + while swarm.next().now_or_never().is_some() {} + let addr = Swarm::listeners(&swarm).next().unwrap().clone(); + Self { + peer_id, + addr, + store, + swarm, + } + } + + fn add_address(&mut self, peer: &Peer) { + self.swarm + .behaviour_mut() + .add_address(&peer.peer_id, peer.addr.clone()); + } + + fn store(&mut self) -> impl std::ops::DerefMut<Target = FnvHashMap<Cid, Vec<u8>>> + '_ { + self.store.0.lock().unwrap() + } + + fn swarm(&mut self) -> &mut Swarm<Bitswap<DefaultParams>> { + &mut self.swarm + } + + fn spawn(mut self, name: &'static str) -> PeerId { + let peer_id = self.peer_id; + task::spawn(async move { + loop { + let event = self.swarm.next().await; + tracing::debug!("{}: {:?}", name, event); + } + }); + peer_id + } + + async fn next(&mut self) -> Option<BitswapEvent> { + loop { + let ev = self.swarm.next().await?; + if let SwarmEvent::Behaviour(event) = ev { + return Some(event); + } + } + } + } + + fn assert_progress(event: Option<BitswapEvent>, id: QueryId, missing: usize) { + if let Some(BitswapEvent::Progress(id2, missing2)) = event { + assert_eq!(id2, id); + assert_eq!(missing2, missing); + } else { + panic!("{:?} is not a progress event", event); + } + } + + fn assert_complete_ok(event: Option<BitswapEvent>, id: QueryId) { + if let Some(BitswapEvent::Complete(id2, Ok(()))) = event { + assert_eq!(id2, id); + } else { + panic!("{:?} is not a complete event", event); + } + } + + #[async_std::test] + async fn test_bitswap_get() { + tracing_try_init(); + let mut peer1 = Peer::new(); + let mut peer2 = Peer::new(); + peer2.add_address(&peer1); + + let block = create_block(ipld!(&b"hello world"[..])); + peer1.store().insert(*block.cid(), block.data().to_vec()); + let peer1 = peer1.spawn("peer1"); + + let id = peer2 + .swarm() + .behaviour_mut() + .get(*block.cid(), std::iter::once(peer1)); + + assert_complete_ok(peer2.next().await, id); + } + + #[async_std::test] + async fn test_bitswap_cancel_get() { + tracing_try_init(); + let mut peer1 = Peer::new(); + let mut peer2 = Peer::new(); + peer2.add_address(&peer1); + + let block = create_block(ipld!(&b"hello world"[..])); + peer1.store().insert(*block.cid(), block.data().to_vec()); + let peer1 = peer1.spawn("peer1"); + + let id = peer2 + .swarm() + .behaviour_mut() + .get(*block.cid(), std::iter::once(peer1)); + peer2.swarm().behaviour_mut().cancel(id); + let res = peer2.next().now_or_never(); + println!("{:?}", res); + assert!(res.is_none()); + } + + #[async_std::test] + async fn test_bitswap_sync() { + tracing_try_init(); + let mut peer1 = Peer::new(); + let mut peer2 = Peer::new(); + peer2.add_address(&peer1); + + let b0 = create_block(ipld!({ + "n": 0, + })); + let b1 = create_block(ipld!({ + "prev": b0.cid(), + "n": 1, + })); + let b2 = create_block(ipld!({ + "prev": b1.cid(), + "n": 2, + })); + peer1.store().insert(*b0.cid(), b0.data().to_vec()); + peer1.store().insert(*b1.cid(), b1.data().to_vec()); + peer1.store().insert(*b2.cid(), b2.data().to_vec()); + let peer1 = peer1.spawn("peer1"); + + let id = + peer2 + .swarm() + .behaviour_mut() + .sync(*b2.cid(), vec![peer1], std::iter::once(*b2.cid())); + + assert_progress(peer2.next().await, id, 1); + assert_progress(peer2.next().await, id, 1); + + assert_complete_ok(peer2.next().await, id); + } + + #[async_std::test] + async fn test_bitswap_cancel_sync() { + tracing_try_init(); + let mut peer1 = Peer::new(); + let mut peer2 = Peer::new(); + peer2.add_address(&peer1); + + let block = create_block(ipld!(&b"hello world"[..])); + peer1.store().insert(*block.cid(), block.data().to_vec()); + let peer1 = peer1.spawn("peer1"); + + let id = peer2.swarm().behaviour_mut().sync( + *block.cid(), + vec![peer1], + std::iter::once(*block.cid()), + ); + peer2.swarm().behaviour_mut().cancel(id); + let res = peer2.next().now_or_never(); + println!("{:?}", res); + assert!(res.is_none()); + } + + #[cfg(feature = "compat")] + #[async_std::test] + async fn compat_test() { + tracing_try_init(); + let cid: Cid = "QmP8njGuyiw9cjkhwHD9nZhyBTHufXFanAvZgcy9xYoWiB" + .parse() + .unwrap(); + let peer_id: PeerId = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + .parse() + .unwrap(); + let multiaddr: Multiaddr = "/ip4/104.131.131.82/tcp/4001".parse().unwrap(); + + let mut peer = Peer::new(); + peer.swarm() + .behaviour_mut() + .add_address(&peer_id, multiaddr); + let id = peer + .swarm() + .behaviour_mut() + .get(cid, std::iter::once(peer_id)); + assert_complete_ok(peer.next().await, id); + } +} diff --git a/ext/libp2p-bitswap/src/compat/bitswap_pb.proto b/ext/libp2p-bitswap/src/compat/bitswap_pb.proto new file mode 100644 index 000000000..9aae7866e --- /dev/null +++ b/ext/libp2p-bitswap/src/compat/bitswap_pb.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +package bitswap_pb; + +message Message { + + message Wantlist { + enum WantType { + Block = 0; + Have = 1; + } + + message Entry { + bytes block = 1; // the block cid (cidV0 in bitswap 1.0.0, cidV1 in bitswap 1.1.0) + int32 priority = 2; // the priority (normalized). default to 1 + bool cancel = 3; // whether this revokes an entry + WantType wantType = 4; // Note: defaults to enum 0, ie Block + bool sendDontHave = 5; // Note: defaults to false + } + + repeated Entry entries = 1; // a list of wantlist entries + bool full = 2; // whether this is the full wantlist. default to false + } + + message Block { + bytes prefix = 1; // CID prefix (cid version, multicodec and multihash prefix (type + length) + bytes data = 2; + } + + enum BlockPresenceType { + Have = 0; + DontHave = 1; + } + message BlockPresence { + bytes cid = 1; + BlockPresenceType type = 2; + } + + Wantlist wantlist = 1; + repeated bytes blocks = 2; // used to send Blocks in bitswap 1.0.0 + repeated Block payload = 3; // used to send Blocks in bitswap 1.1.0 + repeated BlockPresence blockPresences = 4; + int32 pendingBytes = 5; +} diff --git a/ext/libp2p-bitswap/src/compat/message.rs b/ext/libp2p-bitswap/src/compat/message.rs new file mode 100644 index 000000000..0c3f1f944 --- /dev/null +++ b/ext/libp2p-bitswap/src/compat/message.rs @@ -0,0 +1,107 @@ +use crate::compat::other; +use crate::compat::prefix::Prefix; +use crate::protocol::{BitswapRequest, BitswapResponse, RequestType}; +use libipld::Cid; +use prost::Message; +use std::convert::TryFrom; +use std::io; + +mod bitswap_pb { + include!(concat!(env!("OUT_DIR"), "/bitswap_pb.rs")); +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum CompatMessage { + Request(BitswapRequest), + Response(Cid, BitswapResponse), +} + +impl CompatMessage { + pub fn to_bytes(&self) -> io::Result<Vec<u8>> { + let mut msg = bitswap_pb::Message::default(); + match self { + CompatMessage::Request(BitswapRequest { ty, cid }) => { + let mut wantlist = bitswap_pb::message::Wantlist::default(); + let entry = bitswap_pb::message::wantlist::Entry { + block: cid.to_bytes(), + want_type: match ty { + RequestType::Have => bitswap_pb::message::wantlist::WantType::Have, + RequestType::Block => bitswap_pb::message::wantlist::WantType::Block, + } as _, + send_dont_have: true, + cancel: false, + priority: 1, + }; + wantlist.entries.push(entry); + msg.wantlist = Some(wantlist); + } + CompatMessage::Response(cid, BitswapResponse::Have(have)) => { + let block_presence = bitswap_pb::message::BlockPresence { + cid: cid.to_bytes(), + r#type: if *have { + bitswap_pb::message::BlockPresenceType::Have + } else { + bitswap_pb::message::BlockPresenceType::DontHave + } as _, + }; + msg.block_presences.push(block_presence); + } + CompatMessage::Response(cid, BitswapResponse::Block(bytes)) => { + let payload = bitswap_pb::message::Block { + prefix: Prefix::from(cid).to_bytes(), + data: bytes.to_vec(), + }; + msg.payload.push(payload); + } + } + let mut bytes = Vec::with_capacity(msg.encoded_len()); + msg.encode(&mut bytes).map_err(other)?; + Ok(bytes) + } + + pub fn from_bytes(bytes: &[u8]) -> io::Result<Vec<Self>> { + let msg = bitswap_pb::Message::decode(bytes)?; + let mut parts = vec![]; + for entry in msg.wantlist.unwrap_or_default().entries { + if !entry.send_dont_have { + tracing::error!("message hasn't set `send_dont_have`: skipping"); + continue; + } + let cid = Cid::try_from(entry.block).map_err(other)?; + let ty = match entry.want_type { + ty if bitswap_pb::message::wantlist::WantType::Have as i32 == ty => { + RequestType::Have + } + ty if bitswap_pb::message::wantlist::WantType::Block as i32 == ty => { + RequestType::Block + } + _ => { + tracing::error!("invalid request type: skipping"); + continue; + } + }; + parts.push(CompatMessage::Request(BitswapRequest { ty, cid })); + } + for payload in msg.payload { + let prefix = Prefix::new(&payload.prefix)?; + let cid = prefix.to_cid(&payload.data)?; + parts.push(CompatMessage::Response( + cid, + BitswapResponse::Block(payload.data.to_vec()), + )); + } + for presence in msg.block_presences { + let cid = Cid::try_from(presence.cid).map_err(other)?; + let have = match presence.r#type { + ty if bitswap_pb::message::BlockPresenceType::Have as i32 == ty => true, + ty if bitswap_pb::message::BlockPresenceType::DontHave as i32 == ty => false, + _ => { + tracing::error!("invalid block presence type: skipping"); + continue; + } + }; + parts.push(CompatMessage::Response(cid, BitswapResponse::Have(have))); + } + Ok(parts) + } +} diff --git a/ext/libp2p-bitswap/src/compat/mod.rs b/ext/libp2p-bitswap/src/compat/mod.rs new file mode 100644 index 000000000..b3400c2a5 --- /dev/null +++ b/ext/libp2p-bitswap/src/compat/mod.rs @@ -0,0 +1,10 @@ +mod message; +mod prefix; +mod protocol; + +pub use message::CompatMessage; +pub use protocol::{CompatProtocol, InboundMessage}; + +fn other<E: std::error::Error + Send + Sync + 'static>(e: E) -> std::io::Error { + std::io::Error::new(std::io::ErrorKind::Other, e) +} diff --git a/ext/libp2p-bitswap/src/compat/prefix.rs b/ext/libp2p-bitswap/src/compat/prefix.rs new file mode 100644 index 000000000..aeaab7d51 --- /dev/null +++ b/ext/libp2p-bitswap/src/compat/prefix.rs @@ -0,0 +1,71 @@ +use crate::compat::other; +use libipld::cid::{Cid, Version}; +use libipld::multihash::{Code, MultihashDigest}; +use std::convert::TryFrom; +use std::io::Result; +use unsigned_varint::{decode as varint_decode, encode as varint_encode}; + +/// Prefix represents all metadata of a CID, without the actual content. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Prefix { + /// The version of CID. + pub version: Version, + /// The codec of CID. + pub codec: u64, + /// The multihash type of CID. + pub mh_type: u64, + /// The multihash length of CID. + pub mh_len: usize, +} + +impl Prefix { + /// Create a new prefix from encoded bytes. + pub fn new(data: &[u8]) -> Result<Prefix> { + let (raw_version, remain) = varint_decode::u64(data).map_err(other)?; + let version = Version::try_from(raw_version).map_err(other)?; + let (codec, remain) = varint_decode::u64(remain).map_err(other)?; + let (mh_type, remain) = varint_decode::u64(remain).map_err(other)?; + let (mh_len, _remain) = varint_decode::usize(remain).map_err(other)?; + Ok(Prefix { + version, + codec, + mh_type, + mh_len, + }) + } + + /// Convert the prefix to encoded bytes. + pub fn to_bytes(&self) -> Vec<u8> { + let mut res = Vec::with_capacity(4); + let mut buf = varint_encode::u64_buffer(); + let version = varint_encode::u64(self.version.into(), &mut buf); + res.extend_from_slice(version); + let mut buf = varint_encode::u64_buffer(); + let codec = varint_encode::u64(self.codec, &mut buf); + res.extend_from_slice(codec); + let mut buf = varint_encode::u64_buffer(); + let mh_type = varint_encode::u64(self.mh_type, &mut buf); + res.extend_from_slice(mh_type); + let mut buf = varint_encode::u64_buffer(); + let mh_len = varint_encode::u64(self.mh_len as u64, &mut buf); + res.extend_from_slice(mh_len); + res + } + + /// Create a CID out of the prefix and some data that will be hashed + pub fn to_cid(&self, data: &[u8]) -> Result<Cid> { + let mh = Code::try_from(self.mh_type).map_err(other)?.digest(data); + Cid::new(self.version, self.codec, mh).map_err(other) + } +} + +impl From<&Cid> for Prefix { + fn from(cid: &Cid) -> Self { + Self { + version: cid.version(), + codec: cid.codec(), + mh_type: cid.hash().code(), + mh_len: cid.hash().digest().len(), + } + } +} diff --git a/ext/libp2p-bitswap/src/compat/protocol.rs b/ext/libp2p-bitswap/src/compat/protocol.rs new file mode 100644 index 000000000..557e220b5 --- /dev/null +++ b/ext/libp2p-bitswap/src/compat/protocol.rs @@ -0,0 +1,116 @@ +use crate::compat::{other, CompatMessage}; +use futures::future::BoxFuture; +use futures::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; +use std::{io, iter}; + +// Undocumented, but according to JS we our messages have a max size of 512*1024 +// https://github.com/ipfs/js-ipfs-bitswap/blob/d8f80408aadab94c962f6b88f343eb9f39fa0fcc/src/decision-engine/index.js#L16 +const MAX_BUF_SIZE: usize = 524_288; + +#[derive(Clone, Debug, Default)] +pub struct CompatProtocol; + +impl UpgradeInfo for CompatProtocol { + type Info = &'static [u8]; + type InfoIter = iter::Once<Self::Info>; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(b"/ipfs/bitswap/1.2.0") + } +} + +impl<TSocket> InboundUpgrade<TSocket> for CompatProtocol +where + TSocket: AsyncRead + AsyncWrite + Send + Unpin + 'static, +{ + type Output = InboundMessage; + type Error = io::Error; + type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>; + + fn upgrade_inbound(self, mut socket: TSocket, _info: Self::Info) -> Self::Future { + Box::pin(async move { + let packet = upgrade::read_length_prefixed(&mut socket, MAX_BUF_SIZE) + .await + .map_err(other)?; + socket.close().await?; + let message = CompatMessage::from_bytes(&packet)?; + Ok(InboundMessage(message)) + }) + } +} + +impl UpgradeInfo for CompatMessage { + type Info = &'static [u8]; + type InfoIter = iter::Once<Self::Info>; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(b"/ipfs/bitswap/1.2.0") + } +} + +impl<TSocket> OutboundUpgrade<TSocket> for CompatMessage +where + TSocket: AsyncRead + AsyncWrite + Send + Unpin + 'static, +{ + type Output = (); + type Error = io::Error; + type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>; + + fn upgrade_outbound(self, mut socket: TSocket, _info: Self::Info) -> Self::Future { + Box::pin(async move { + let bytes = self.to_bytes()?; + upgrade::write_length_prefixed(&mut socket, bytes).await?; + socket.close().await?; + Ok(()) + }) + } +} + +#[derive(Debug)] +pub struct InboundMessage(pub Vec<CompatMessage>); + +impl From<()> for InboundMessage { + fn from(_: ()) -> Self { + Self(Default::default()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::protocol::{BitswapRequest, RequestType}; + use async_std::net::{TcpListener, TcpStream}; + use futures::prelude::*; + use libipld::Cid; + use libp2p::core::upgrade; + + #[async_std::test] + async fn test_upgrade() { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let listener_addr = listener.local_addr().unwrap(); + + let server = async move { + let incoming = listener.incoming().into_future().await.0.unwrap().unwrap(); + upgrade::apply_inbound(incoming, CompatProtocol) + .await + .unwrap(); + }; + + let client = async move { + let stream = TcpStream::connect(&listener_addr).await.unwrap(); + upgrade::apply_outbound( + stream, + CompatMessage::Request(BitswapRequest { + ty: RequestType::Have, + cid: Cid::default(), + }), + upgrade::Version::V1, + ) + .await + .unwrap(); + }; + + future::select(Box::pin(server), Box::pin(client)).await; + } +} diff --git a/ext/libp2p-bitswap/src/lib.rs b/ext/libp2p-bitswap/src/lib.rs new file mode 100644 index 000000000..4c10d3e81 --- /dev/null +++ b/ext/libp2p-bitswap/src/lib.rs @@ -0,0 +1,15 @@ +//! Bitswap protocol implementation +#![deny(missing_docs)] +#![deny(warnings)] +#![allow(clippy::derive_partial_eq_without_eq)] + +mod behaviour; +#[cfg(feature = "compat")] +mod compat; +mod protocol; +mod query; +mod stats; + +pub use crate::behaviour::{Bitswap, BitswapConfig, BitswapEvent, BitswapStore, Channel}; +pub use crate::protocol::{BitswapRequest, BitswapResponse}; +pub use crate::query::QueryId; diff --git a/ext/libp2p-bitswap/src/protocol.rs b/ext/libp2p-bitswap/src/protocol.rs new file mode 100644 index 000000000..5209bcc9a --- /dev/null +++ b/ext/libp2p-bitswap/src/protocol.rs @@ -0,0 +1,282 @@ +use async_trait::async_trait; +use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use libipld::cid::Cid; +use libipld::store::StoreParams; +use libp2p::request_response; +use std::convert::TryFrom; +use std::io::{self, Write}; +use std::marker::PhantomData; +use thiserror::Error; +use unsigned_varint::{aio, io::ReadError}; + +// version codec hash size (u64 varint is max 10 bytes) + digest +const MAX_CID_SIZE: usize = 4 * 10 + 64; + +#[derive(Clone, Debug)] +pub struct BitswapProtocol; + +impl AsRef<str> for BitswapProtocol { + fn as_ref(&self) -> &str { + "/ipfs-embed/bitswap/1.0.0" + } +} + +#[derive(Clone)] +pub struct BitswapCodec<P> { + _marker: PhantomData<P>, + buffer: Vec<u8>, +} + +impl<P: StoreParams> Default for BitswapCodec<P> { + fn default() -> Self { + let capacity = usize::max(P::MAX_BLOCK_SIZE, MAX_CID_SIZE) + 1; + debug_assert!(capacity <= u32::MAX as usize); + Self { + _marker: PhantomData, + buffer: Vec::with_capacity(capacity), + } + } +} + +#[async_trait] +impl<P: StoreParams> request_response::Codec for BitswapCodec<P> { + type Protocol = BitswapProtocol; + type Request = BitswapRequest; + type Response = BitswapResponse; + + async fn read_request<T>(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result<Self::Request> + where + T: AsyncRead + Send + Unpin, + { + let msg_len = u32_to_usize(aio::read_u32(&mut *io).await.map_err(|e| match e { + ReadError::Io(e) => e, + err => other(err), + })?); + if msg_len > MAX_CID_SIZE + 1 { + return Err(invalid_data(MessageTooLarge(msg_len))); + } + self.buffer.resize(msg_len, 0); + io.read_exact(&mut self.buffer).await?; + let request = BitswapRequest::from_bytes(&self.buffer).map_err(invalid_data)?; + Ok(request) + } + + async fn read_response<T>( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result<Self::Response> + where + T: AsyncRead + Send + Unpin, + { + let msg_len = u32_to_usize(aio::read_u32(&mut *io).await.map_err(|e| match e { + ReadError::Io(e) => e, + err => other(err), + })?); + if msg_len > P::MAX_BLOCK_SIZE + 1 { + return Err(invalid_data(MessageTooLarge(msg_len))); + } + self.buffer.resize(msg_len, 0); + io.read_exact(&mut self.buffer).await?; + let response = BitswapResponse::from_bytes(&self.buffer).map_err(invalid_data)?; + Ok(response) + } + + async fn write_request<T>( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Send + Unpin, + { + self.buffer.clear(); + req.write_to(&mut self.buffer)?; + if self.buffer.len() > MAX_CID_SIZE + 1 { + return Err(invalid_data(MessageTooLarge(self.buffer.len()))); + } + let mut buf = unsigned_varint::encode::u32_buffer(); + let msg_len = unsigned_varint::encode::u32(self.buffer.len() as u32, &mut buf); + io.write_all(msg_len).await?; + io.write_all(&self.buffer).await?; + Ok(()) + } + + async fn write_response<T>( + &mut self, + _: &Self::Protocol, + io: &mut T, + res: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Send + Unpin, + { + self.buffer.clear(); + res.write_to(&mut self.buffer)?; + if self.buffer.len() > P::MAX_BLOCK_SIZE + 1 { + return Err(invalid_data(MessageTooLarge(self.buffer.len()))); + } + let mut buf = unsigned_varint::encode::u32_buffer(); + let msg_len = unsigned_varint::encode::u32(self.buffer.len() as u32, &mut buf); + io.write_all(msg_len).await?; + io.write_all(&self.buffer).await?; + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum RequestType { + Have, + Block, +} + +/// A request sent to another peer. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct BitswapRequest { + /// type of request: have or block + pub ty: RequestType, + /// CID the request is for + pub cid: Cid, +} + +impl BitswapRequest { + /// write binary representation of the request + pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> { + match self { + BitswapRequest { + ty: RequestType::Have, + cid, + } => { + w.write_all(&[0])?; + cid.write_bytes(&mut *w).map_err(other)?; + } + BitswapRequest { + ty: RequestType::Block, + cid, + } => { + w.write_all(&[1])?; + cid.write_bytes(&mut *w).map_err(other)?; + } + } + Ok(()) + } + + /// read back binary representation of the request + pub fn from_bytes(bytes: &[u8]) -> io::Result<Self> { + let ty = match bytes[0] { + 0 => RequestType::Have, + 1 => RequestType::Block, + c => return Err(invalid_data(UnknownMessageType(c))), + }; + let cid = Cid::try_from(&bytes[1..]).map_err(invalid_data)?; + Ok(Self { ty, cid }) + } +} + +/// Response to a [BitswapRequest] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum BitswapResponse { + /// block presence + Have(bool), + /// block bytes + Block(Vec<u8>), +} + +impl BitswapResponse { + /// write binary representation of the request + pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> { + match self { + BitswapResponse::Have(have) => { + if *have { + w.write_all(&[0])?; + } else { + w.write_all(&[2])?; + } + } + BitswapResponse::Block(data) => { + w.write_all(&[1])?; + w.write_all(data)?; + } + }; + Ok(()) + } + + /// read back binary representation of the request + pub fn from_bytes(bytes: &[u8]) -> io::Result<Self> { + let res = match bytes[0] { + 0 | 2 => BitswapResponse::Have(bytes[0] == 0), + 1 => BitswapResponse::Block(bytes[1..].to_vec()), + c => return Err(invalid_data(UnknownMessageType(c))), + }; + Ok(res) + } +} + +fn invalid_data<E: std::error::Error + Send + Sync + 'static>(e: E) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, e) +} + +fn other<E: std::error::Error + Send + Sync + 'static>(e: E) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) +} + +#[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))] +fn u32_to_usize(n: u32) -> usize { + n as usize +} + +#[derive(Debug, Error)] +#[error("unknown message type {0}")] +pub struct UnknownMessageType(u8); + +#[derive(Debug, Error)] +#[error("message too large {0}")] +pub struct MessageTooLarge(usize); + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use libipld::multihash::Code; + use multihash::MultihashDigest; + + pub fn create_cid(bytes: &[u8]) -> Cid { + let digest = Code::Blake3_256.digest(bytes); + Cid::new_v1(0x55, digest) + } + + #[test] + fn test_request_encode_decode() { + let requests = [ + BitswapRequest { + ty: RequestType::Have, + cid: create_cid(&b"have_request"[..]), + }, + BitswapRequest { + ty: RequestType::Block, + cid: create_cid(&b"block_request"[..]), + }, + ]; + let mut buf = Vec::with_capacity(MAX_CID_SIZE + 1); + for request in &requests { + buf.clear(); + request.write_to(&mut buf).unwrap(); + assert_eq!(&BitswapRequest::from_bytes(&buf).unwrap(), request); + } + } + + #[test] + fn test_response_encode_decode() { + let responses = [ + BitswapResponse::Have(true), + BitswapResponse::Have(false), + BitswapResponse::Block(b"block_response".to_vec()), + ]; + let mut buf = Vec::with_capacity(13 + 1); + for response in &responses { + buf.clear(); + response.write_to(&mut buf).unwrap(); + assert_eq!(&BitswapResponse::from_bytes(&buf).unwrap(), response); + } + } +} diff --git a/ext/libp2p-bitswap/src/query.rs b/ext/libp2p-bitswap/src/query.rs new file mode 100644 index 000000000..1ed276d61 --- /dev/null +++ b/ext/libp2p-bitswap/src/query.rs @@ -0,0 +1,660 @@ +use crate::stats::{REQUESTS_TOTAL, REQUEST_DURATION_SECONDS}; +use fnv::{FnvHashMap, FnvHashSet}; +use libipld::Cid; +use libp2p::PeerId; +use prometheus::HistogramTimer; +use std::collections::VecDeque; + +/// Query id. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct QueryId(u64); + +impl std::fmt::Display for QueryId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +/// Request. +#[derive(Debug, Eq, PartialEq)] +pub enum Request { + /// Have query. + Have(PeerId, Cid), + /// Block query. + Block(PeerId, Cid), + /// Missing blocks query. + MissingBlocks(Cid), +} + +impl std::fmt::Display for Request { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Have(_, _) => write!(f, "have"), + Self::Block(_, _) => write!(f, "block"), + Self::MissingBlocks(_) => write!(f, "missing-blocks"), + } + } +} + +/// Response. +#[derive(Debug)] +pub enum Response { + /// Have query. + Have(PeerId, bool), + /// Block query. + Block(PeerId, bool), + /// Missing blocks query. + MissingBlocks(Vec<Cid>), +} + +impl std::fmt::Display for Response { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Have(_, have) => write!(f, "have {}", have), + Self::Block(_, block) => write!(f, "block {}", block), + Self::MissingBlocks(missing) => write!(f, "missing-blocks {}", missing.len()), + } + } +} + +/// Event emitted by a query. +#[derive(Debug)] +pub enum QueryEvent { + /// A subquery to run. + Request(QueryId, Request), + /// A progress event. + Progress(QueryId, usize), + /// Complete event. + Complete(QueryId, Result<(), Cid>), +} + +#[derive(Debug)] +pub struct Header { + /// Query id. + pub id: QueryId, + /// Root query id. + pub root: QueryId, + /// Parent. + pub parent: Option<QueryId>, + /// Cid. + pub cid: Cid, + /// Timer. + #[allow(dead_code)] + pub timer: HistogramTimer, + /// Type. + pub label: &'static str, +} + +impl Drop for Header { + fn drop(&mut self) { + REQUESTS_TOTAL.with_label_values(&[self.label]).inc(); + } +} + +/// Query. +#[derive(Debug)] +struct Query { + /// Header. + hdr: Header, + /// State. + state: State, +} + +#[derive(Debug)] +enum State { + None, + Get(GetState), + Sync(SyncState), +} + +#[derive(Debug, Default)] +struct GetState { + have: FnvHashSet<QueryId>, + block: Option<QueryId>, + providers: Vec<PeerId>, +} + +#[derive(Debug, Default)] +struct SyncState { + missing: FnvHashSet<QueryId>, + children: FnvHashSet<QueryId>, + providers: Vec<PeerId>, +} + +enum Transition<S, C> { + Next(S), + Complete(C), +} + +#[derive(Default)] +pub struct QueryManager { + id_counter: u64, + queries: FnvHashMap<QueryId, Query>, + events: VecDeque<QueryEvent>, +} + +impl QueryManager { + /// Start a new subquery. + fn start_query( + &mut self, + root: QueryId, + parent: Option<QueryId>, + cid: Cid, + req: Request, + label: &'static str, + ) -> QueryId { + let timer = REQUEST_DURATION_SECONDS + .with_label_values(&[label]) + .start_timer(); + let id = QueryId(self.id_counter); + self.id_counter += 1; + let query = Query { + hdr: Header { + id, + root, + parent, + cid, + timer, + label, + }, + state: State::None, + }; + self.queries.insert(id, query); + tracing::trace!("{} {} {}", root, id, req); + self.events.push_back(QueryEvent::Request(id, req)); + id + } + + /// Starts a new have query to ask a peer if it has a block. + fn have(&mut self, root: QueryId, parent: QueryId, peer_id: PeerId, cid: Cid) -> QueryId { + self.start_query(root, Some(parent), cid, Request::Have(peer_id, cid), "have") + } + + /// Starts a new block query to request a block from a peer. + fn block(&mut self, root: QueryId, parent: QueryId, peer_id: PeerId, cid: Cid) -> QueryId { + self.start_query( + root, + Some(parent), + cid, + Request::Block(peer_id, cid), + "block", + ) + } + + /// Starts a query to determine the missing blocks of a dag. + fn missing_blocks(&mut self, parent: QueryId, cid: Cid) -> QueryId { + self.start_query( + parent, + Some(parent), + cid, + Request::MissingBlocks(cid), + "missing-blocks", + ) + } + + /// Starts a query to locate and retrieve a block. Panics if no providers are supplied. + pub fn get( + &mut self, + parent: Option<QueryId>, + cid: Cid, + providers: impl Iterator<Item = PeerId>, + ) -> QueryId { + let timer = REQUEST_DURATION_SECONDS + .with_label_values(&["get"]) + .start_timer(); + let id = QueryId(self.id_counter); + self.id_counter += 1; + let root = parent.unwrap_or(id); + tracing::trace!("{} {} get", root, id); + let mut state = GetState::default(); + for peer in providers { + if state.block.is_none() { + state.block = Some(self.block(root, id, peer, cid)); + } else { + state.have.insert(self.have(root, id, peer, cid)); + } + } + assert!(state.block.is_some()); + let query = Query { + hdr: Header { + id, + root, + parent, + cid, + timer, + label: "get", + }, + state: State::Get(state), + }; + self.queries.insert(id, query); + id + } + + /// Starts a query to recursively retrieve a dag. The missing blocks are the first + /// blocks that need to be retrieved. + pub fn sync( + &mut self, + cid: Cid, + providers: Vec<PeerId>, + missing: impl Iterator<Item = Cid>, + ) -> QueryId { + let timer = REQUEST_DURATION_SECONDS + .with_label_values(&["sync"]) + .start_timer(); + let id = QueryId(self.id_counter); + self.id_counter += 1; + tracing::trace!("{} {} sync", id, id); + let mut state = SyncState::default(); + for cid in missing { + state + .missing + .insert(self.get(Some(id), cid, providers.iter().copied())); + } + if state.missing.is_empty() { + state.children.insert(self.missing_blocks(id, cid)); + } + state.providers = providers; + let query = Query { + hdr: Header { + id, + root: id, + parent: None, + cid, + timer, + label: "sync", + }, + state: State::Sync(state), + }; + self.queries.insert(id, query); + id + } + + /// Cancels an in progress query. + pub fn cancel(&mut self, root: QueryId) -> bool { + let query = if let Some(query) = self.queries.remove(&root) { + query + } else { + return false; + }; + let queries = &self.queries; + self.events.retain(|event| { + let (id, req) = match event { + QueryEvent::Request(id, req) => (id, req), + QueryEvent::Progress(id, _) => return *id != root, + QueryEvent::Complete(_, _) => return true, + }; + if queries.get(id).map(|q| q.hdr.root) != Some(root) { + return true; + } + tracing::trace!("{} {} {} cancel", root, id, req); + false + }); + match query.state { + State::Get(_) => { + tracing::trace!("{} {} get cancel", root, root); + true + } + State::Sync(state) => { + for id in state.missing { + tracing::trace!("{} {} get cancel", root, id); + self.queries.remove(&id); + } + tracing::trace!("{} {} sync cancel", root, root); + true + } + State::None => { + self.queries.insert(root, query); + false + } + } + } + + /// Advances a get query state machine using a transition function. + fn get_query<F>(&mut self, id: QueryId, f: F) + where + F: FnOnce(&mut Self, &Header, GetState) -> Transition<GetState, Result<(), Cid>>, + { + if let Some(mut parent) = self.queries.remove(&id) { + let state = if let State::Get(state) = parent.state { + state + } else { + return; + }; + match f(self, &parent.hdr, state) { + Transition::Next(state) => { + parent.state = State::Get(state); + self.queries.insert(id, parent); + } + Transition::Complete(res) => { + match res { + Ok(()) => tracing::trace!("{} {} get ok", parent.hdr.root, parent.hdr.id), + Err(_) => tracing::trace!("{} {} get err", parent.hdr.root, parent.hdr.id), + } + self.recv_get(parent.hdr, res); + } + } + } + } + + /// Advances a sync query state machine using a transition function. + fn sync_query<F>(&mut self, id: QueryId, f: F) + where + F: FnOnce(&mut Self, &Header, SyncState) -> Transition<SyncState, Result<(), Cid>>, + { + if let Some(mut parent) = self.queries.remove(&id) { + let state = if let State::Sync(state) = parent.state { + state + } else { + return; + }; + match f(self, &parent.hdr, state) { + Transition::Next(state) => { + parent.state = State::Sync(state); + self.queries.insert(id, parent); + } + Transition::Complete(res) => { + if res.is_ok() { + tracing::trace!("{} {} sync ok", parent.hdr.root, parent.hdr.id); + } else { + tracing::trace!("{} {} sync err", parent.hdr.root, parent.hdr.id); + } + self.recv_sync(parent.hdr, res); + } + } + } + } + + /// Processes the response of a have query. + /// + /// Marks the in progress query as complete and updates the set of peers that have + /// a block. If there isn't an in progress block query a new block query will be + /// started. If no block query can be started either a provider query is started or + /// the get query is marked as complete with a block-not-found error. + fn recv_have(&mut self, query: Header, peer_id: PeerId, have: bool) { + self.get_query(query.parent.unwrap(), |mgr, parent, mut state| { + state.have.remove(&query.id); + if state.block == Some(query.id) { + state.block = None; + } + if have { + state.providers.push(peer_id); + } + if state.block.is_none() && !state.providers.is_empty() { + state.block = Some(mgr.block( + parent.root, + parent.id, + state.providers.pop().unwrap(), + query.cid, + )); + } + if state.have.is_empty() && state.block.is_none() && state.providers.is_empty() { + if state.providers.is_empty() { + return Transition::Complete(Err(query.cid)); + } else { + return Transition::Complete(Ok(())); + } + } + Transition::Next(state) + }); + } + + /// Processes the response of a block query. + /// + /// Either completes the get query or processes it like a have query response. + fn recv_block(&mut self, query: Header, peer_id: PeerId, block: bool) { + if block { + self.get_query(query.parent.unwrap(), |_mgr, _parent, mut state| { + state.providers.push(peer_id); + Transition::Complete(Ok(())) + }); + } else { + self.recv_have(query, peer_id, block); + } + } + + /// Processes the response of a missing blocks query. + /// + /// Starts a get query for each missing block. If there are no in progress queries + /// the sync query is marked as complete. + fn recv_missing_blocks(&mut self, query: Header, missing: Vec<Cid>) { + let mut num_missing = 0; + let num_missing_ref = &mut num_missing; + self.sync_query(query.parent.unwrap(), |mgr, parent, mut state| { + state.children.remove(&query.id); + for cid in missing { + state.missing.insert(mgr.get( + Some(parent.root), + cid, + state.providers.iter().copied(), + )); + } + *num_missing_ref = state.missing.len(); + if state.missing.is_empty() && state.children.is_empty() { + Transition::Complete(Ok(())) + } else { + Transition::Next(state) + } + }); + if num_missing != 0 { + self.events + .push_back(QueryEvent::Progress(query.root, num_missing)); + } + } + + /// Processes the response of a get query. + /// + /// If it is part of a sync query a new missing blocks query is started. Otherwise + /// the get query emits a `complete` event. + fn recv_get(&mut self, query: Header, res: Result<(), Cid>) { + if let Some(id) = query.parent { + self.sync_query(id, |mgr, parent, mut state| { + state.missing.remove(&query.id); + if res.is_err() { + Transition::Complete(res) + } else { + state + .children + .insert(mgr.missing_blocks(parent.root, query.cid)); + Transition::Next(state) + } + }); + } else { + self.events.push_back(QueryEvent::Complete(query.id, res)); + } + } + + /// Processes the response of a sync query. + /// + /// The sync query emits a `complete` event. + fn recv_sync(&mut self, query: Header, res: Result<(), Cid>) { + self.events.push_back(QueryEvent::Complete(query.id, res)); + } + + /// Dispatches the response to a query handler. + pub fn inject_response(&mut self, id: QueryId, res: Response) { + let query = if let Some(query) = self.queries.remove(&id) { + query.hdr + } else { + return; + }; + tracing::trace!("{} {} {}", query.root, query.id, res); + match res { + Response::Have(peer, have) => { + self.recv_have(query, peer, have); + } + Response::Block(peer, block) => { + self.recv_block(query, peer, block); + } + Response::MissingBlocks(cids) => { + self.recv_missing_blocks(query, cids); + } + } + } + + /// Returns the header of a query. + pub fn query_info(&self, id: QueryId) -> Option<&Header> { + self.queries.get(&id).map(|q| &q.hdr) + } + + /// Retrieves the next query event. + pub fn next(&mut self) -> Option<QueryEvent> { + self.events.pop_front() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn tracing_try_init() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .ok(); + } + + fn gen_peers(n: usize) -> Vec<PeerId> { + let mut peers = Vec::with_capacity(n); + for _ in 0..n { + peers.push(PeerId::random()); + } + peers + } + + fn assert_request(event: Option<QueryEvent>, req: Request) -> QueryId { + if let Some(QueryEvent::Request(id, req2)) = event { + assert_eq!(req2, req); + id + } else { + panic!("{:?} is not a request", event); + } + } + + fn assert_complete(event: Option<QueryEvent>, id: QueryId, res: Result<(), Cid>) { + if let Some(QueryEvent::Complete(id2, res2)) = event { + assert_eq!(id, id2); + assert_eq!(res, res2); + } else { + panic!("{:?} is not a complete event", event); + } + } + + #[test] + fn test_get_query_block_not_found() { + let mut mgr = QueryManager::default(); + let initial_set = gen_peers(3); + let cid = Cid::default(); + + let id = mgr.get(None, cid, initial_set.iter().copied()); + + let id1 = assert_request(mgr.next(), Request::Block(initial_set[0], cid)); + let id2 = assert_request(mgr.next(), Request::Have(initial_set[1], cid)); + let id3 = assert_request(mgr.next(), Request::Have(initial_set[2], cid)); + + mgr.inject_response(id1, Response::Have(initial_set[0], false)); + mgr.inject_response(id2, Response::Have(initial_set[1], false)); + mgr.inject_response(id3, Response::Have(initial_set[2], false)); + + assert_complete(mgr.next(), id, Err(cid)); + } + + #[test] + fn test_cid_query_block_found() { + let mut mgr = QueryManager::default(); + let initial_set = gen_peers(3); + let cid = Cid::default(); + + let id = mgr.get(None, cid, initial_set.iter().copied()); + + let id1 = assert_request(mgr.next(), Request::Block(initial_set[0], cid)); + let id2 = assert_request(mgr.next(), Request::Have(initial_set[1], cid)); + let id3 = assert_request(mgr.next(), Request::Have(initial_set[2], cid)); + + mgr.inject_response(id1, Response::Block(initial_set[0], true)); + mgr.inject_response(id2, Response::Have(initial_set[1], false)); + mgr.inject_response(id3, Response::Have(initial_set[2], false)); + + assert_complete(mgr.next(), id, Ok(())); + } + + #[test] + fn test_get_query_gets_from_spare_if_block_request_fails() { + let mut mgr = QueryManager::default(); + let initial_set = gen_peers(3); + let cid = Cid::default(); + + let id = mgr.get(None, cid, initial_set.iter().copied()); + + let id1 = assert_request(mgr.next(), Request::Block(initial_set[0], cid)); + let id2 = assert_request(mgr.next(), Request::Have(initial_set[1], cid)); + let id3 = assert_request(mgr.next(), Request::Have(initial_set[2], cid)); + + mgr.inject_response(id1, Response::Block(initial_set[0], false)); + mgr.inject_response(id2, Response::Have(initial_set[1], true)); + mgr.inject_response(id3, Response::Have(initial_set[2], false)); + + let id1 = assert_request(mgr.next(), Request::Block(initial_set[1], cid)); + mgr.inject_response(id1, Response::Block(initial_set[1], true)); + + assert_complete(mgr.next(), id, Ok(())); + } + + #[test] + fn test_get_query_gets_from_spare_if_block_request_fails_after_have_is_received() { + let mut mgr = QueryManager::default(); + let initial_set = gen_peers(3); + let cid = Cid::default(); + + let id = mgr.get(None, cid, initial_set.iter().copied()); + + let id1 = assert_request(mgr.next(), Request::Block(initial_set[0], cid)); + let id2 = assert_request(mgr.next(), Request::Have(initial_set[1], cid)); + let id3 = assert_request(mgr.next(), Request::Have(initial_set[2], cid)); + + mgr.inject_response(id1, Response::Block(initial_set[0], false)); + mgr.inject_response(id2, Response::Have(initial_set[1], true)); + mgr.inject_response(id3, Response::Have(initial_set[2], true)); + + let id1 = assert_request(mgr.next(), Request::Block(initial_set[1], cid)); + mgr.inject_response(id1, Response::Block(initial_set[1], false)); + + let id1 = assert_request(mgr.next(), Request::Block(initial_set[2], cid)); + mgr.inject_response(id1, Response::Block(initial_set[2], true)); + + assert_complete(mgr.next(), id, Ok(())); + } + + #[test] + fn test_sync_query() { + tracing_try_init(); + let mut mgr = QueryManager::default(); + let providers = gen_peers(3); + let cid = Cid::default(); + + let id = mgr.sync(cid, providers.clone(), std::iter::once(cid)); + + let id1 = assert_request(mgr.next(), Request::Block(providers[0], cid)); + let id2 = assert_request(mgr.next(), Request::Have(providers[1], cid)); + let id3 = assert_request(mgr.next(), Request::Have(providers[2], cid)); + + mgr.inject_response(id1, Response::Block(providers[0], true)); + mgr.inject_response(id2, Response::Have(providers[1], false)); + mgr.inject_response(id3, Response::Have(providers[2], false)); + + let id1 = assert_request(mgr.next(), Request::MissingBlocks(cid)); + mgr.inject_response(id1, Response::MissingBlocks(vec![])); + + assert_complete(mgr.next(), id, Ok(())); + } + + #[test] + fn test_sync_query_empty() { + tracing_try_init(); + let mut mgr = QueryManager::default(); + let cid = Cid::default(); + let id = mgr.sync(cid, vec![], std::iter::empty()); + let id1 = assert_request(mgr.next(), Request::MissingBlocks(cid)); + mgr.inject_response(id1, Response::MissingBlocks(vec![])); + assert_complete(mgr.next(), id, Ok(())); + } +} diff --git a/ext/libp2p-bitswap/src/stats.rs b/ext/libp2p-bitswap/src/stats.rs new file mode 100644 index 000000000..432a804eb --- /dev/null +++ b/ext/libp2p-bitswap/src/stats.rs @@ -0,0 +1,86 @@ +use lazy_static::lazy_static; +use prometheus::{HistogramOpts, HistogramVec, IntCounter, IntCounterVec, Opts}; + +lazy_static! { + pub static ref REQUESTS_TOTAL: IntCounterVec = IntCounterVec::new( + Opts::new( + "bitswap_requests_total", + "Number of bitswap requests labelled by type and result.", + ), + &["type"], + ) + .unwrap(); + pub static ref REQUEST_DURATION_SECONDS: HistogramVec = HistogramVec::new( + HistogramOpts::new( + "bitswap_request_duration_seconds", + "Duration of bitswap requests labelled by request type", + ), + &["type"], + ) + .unwrap(); + pub static ref REQUESTS_CANCELED: IntCounter = IntCounter::new( + "bitswap_requests_canceled_total", + "Number of canceled requests", + ) + .unwrap(); + pub static ref BLOCK_NOT_FOUND: IntCounter = IntCounter::new( + "bitswap_block_not_found_total", + "Number of block not found errors.", + ) + .unwrap(); + pub static ref PROVIDERS_TOTAL: IntCounter = IntCounter::new( + "bitswap_providers_total", + r#"Number of providers total. Using the number of provider requests, the average + number of providers per request can be computed."# + ) + .unwrap(); + pub static ref MISSING_BLOCKS_TOTAL: IntCounter = IntCounter::new( + "bitswap_missing_blocks_total", + r#"Number of missing blocks total. Using the number of missing blocks requests, the + average number of missing blocks per request can be computed."# + ) + .unwrap(); + pub static ref RECEIVED_BLOCK_BYTES: IntCounter = + IntCounter::new("bitswap_received_block_bytes", "Number of received bytes.",).unwrap(); + pub static ref RECEIVED_INVALID_BLOCK_BYTES: IntCounter = IntCounter::new( + "bitswap_received_invalid_block_bytes", + "Number of received bytes that didn't match the hash.", + ) + .unwrap(); + pub static ref SENT_BLOCK_BYTES: IntCounter = + IntCounter::new("bitswap_sent_block_bytes", "Number of sent block bytes.",).unwrap(); + pub static ref RESPONSES_TOTAL: IntCounterVec = IntCounterVec::new( + Opts::new( + "bitswap_responses_total", + "Number of bitswap responses sent to peers.", + ), + &["type"], + ) + .unwrap(); + pub static ref THROTTLED_INBOUND: IntCounter = IntCounter::new( + "bitswap_throttled_too_many_inbound_total", + "Number of too many inbound events.", + ) + .unwrap(); + pub static ref THROTTLED_OUTBOUND: IntCounter = IntCounter::new( + "bitswap_throttled_resume_send_total", + "Number of resume send events.", + ) + .unwrap(); + pub static ref OUTBOUND_FAILURE: IntCounterVec = IntCounterVec::new( + Opts::new( + "bitswap_outbound_failures_total", + "Number of outbound failures.", + ), + &["type"], + ) + .unwrap(); + pub static ref INBOUND_FAILURE: IntCounterVec = IntCounterVec::new( + Opts::new( + "bitswap_inbound_failures_total", + "Number of inbound failures.", + ), + &["type"], + ) + .unwrap(); +} diff --git a/ipld/resolver/Cargo.toml b/ipld/resolver/Cargo.toml index 8c5e8a69c..dac26ec58 100644 --- a/ipld/resolver/Cargo.toml +++ b/ipld/resolver/Cargo.toml @@ -12,7 +12,6 @@ async-trait = { workspace = true } base64 = { workspace = true } blake2b_simd = { workspace = true } bloom = { workspace = true } -gcra = { workspace = true } lazy_static = { workspace = true } libipld = { workspace = true } libp2p = { workspace = true } @@ -35,6 +34,7 @@ fvm_ipld_blockstore = { workspace = true, optional = true } ipc-api = { path = "../../ipc/api", default-features = false } ipc-observability = { workspace = true } +gcra = { path = "../../ext/gcra-rs" } [dev-dependencies] cid = { workspace = true }