diff --git a/Cargo.lock b/Cargo.lock index 50ce03c38..cb1794961 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -273,7 +274,7 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "k256", - "rand", + "rand 0.8.5", "serde_json", "tempfile", "thiserror 1.0.65", @@ -301,7 +302,7 @@ dependencies = [ "keccak-asm", "paste", "proptest", - "rand", + "rand 0.8.5", "ruint", "rustc-hash 2.0.0", "serde", @@ -469,7 +470,7 @@ dependencies = [ "alloy-signer", "async-trait", "k256", - "rand", + "rand 0.8.5", "thiserror 1.0.65", ] @@ -595,6 +596,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.17" @@ -684,7 +691,7 @@ dependencies = [ "ark-serialize 0.3.0", "ark-std 0.3.0", "derivative", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "paste", "rustc_version 0.3.3", @@ -704,7 +711,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "paste", "rustc_version 0.4.1", @@ -737,7 +744,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "quote", "syn 1.0.109", @@ -749,7 +756,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "proc-macro2", "quote", @@ -810,7 +817,7 @@ dependencies = [ "ark-serialize-derive", "ark-std 0.4.0", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", ] [[package]] @@ -831,7 +838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits 0.2.19", - "rand", + "rand 0.8.5", ] [[package]] @@ -841,7 +848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits 0.2.19", - "rand", + "rand 0.8.5", ] [[package]] @@ -1120,6 +1127,15 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.4.0", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -1244,9 +1260,9 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" dependencies = [ - "autocfg", + "autocfg 1.4.0", "libm", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", ] @@ -1368,14 +1384,14 @@ dependencies = [ "itertools 0.10.5", "keccak", "log", - "num-bigint", + "num-bigint 0.4.6", "num-integer", - "num-rational", + "num-rational 0.4.2", "num-traits 0.2.19", "once_cell", "paste", "phf", - "rand", + "rand 0.8.5", "rstest 0.17.0", "serde", "serde_json", @@ -1453,6 +1469,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytemuck" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" + [[package]] name = "byteorder" version = "1.5.0" @@ -1547,7 +1569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f460db3bb8e8589812495fdca7301e9674b3a2c81f2380e9c07d914979a42" dependencies = [ "lazy_static", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -1560,7 +1582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a93dedd19b8edf685798f1f12e4e0ac21ac196ea5262c300783f69f3fa0cb28b" dependencies = [ "lazy_static", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -1573,7 +1595,7 @@ source = "git+https://github.com/starkware-libs/cairo?tag=v1.0.0-alpha.6#439da05 dependencies = [ "cairo-lang-utils 1.0.0-alpha.6", "indoc 1.0.9", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "serde", "thiserror 1.0.65", @@ -1586,7 +1608,7 @@ source = "git+https://github.com/starkware-libs/cairo?tag=v1.0.0-rc0#05867c82de4 dependencies = [ "cairo-lang-utils 1.0.0-rc0", "indoc 2.0.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "serde", "thiserror 1.0.65", @@ -1600,7 +1622,7 @@ checksum = "076a07a68b7f4b3f04e0e23f1e4bee42358abab54929b7842b42108bdb76a164" dependencies = [ "cairo-lang-utils 1.1.1", "indoc 2.0.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "serde", "thiserror 1.0.65", @@ -1614,7 +1636,7 @@ checksum = "fd4d6659539ace9649c8e8a7434e51b0c50a7a700111d0a2b967dde220ddff49" dependencies = [ "cairo-lang-utils 2.8.4", "indoc 2.0.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "parity-scale-codec", "serde", @@ -1998,7 +2020,7 @@ dependencies = [ "indexmap 1.9.3", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.1.24", @@ -2022,7 +2044,7 @@ dependencies = [ "indexmap 1.9.3", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -2047,7 +2069,7 @@ dependencies = [ "indexmap 1.9.3", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -2071,7 +2093,7 @@ dependencies = [ "id-arena", "itertools 0.12.1", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "rust-analyzer-salsa", "smol_str 0.2.2", @@ -2107,7 +2129,7 @@ dependencies = [ "colored", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -2128,7 +2150,7 @@ dependencies = [ "colored", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -2148,7 +2170,7 @@ dependencies = [ "cairo-lang-utils 2.8.4", "colored", "itertools 0.12.1", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "rust-analyzer-salsa", "smol_str 0.2.2", @@ -2344,10 +2366,10 @@ dependencies = [ "cairo-vm", "itertools 0.12.1", "keccak", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", - "rand", + "rand 0.8.5", "sha2", "smol_str 0.2.2", "starknet-types-core 0.1.7 (git+https://github.com/kasarlabs/types-rs.git?branch=feat-deserialize-v0.1.7)", @@ -2370,7 +2392,7 @@ dependencies = [ "id-arena", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "pretty_assertions", "salsa", @@ -2394,7 +2416,7 @@ dependencies = [ "id-arena", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -2417,7 +2439,7 @@ dependencies = [ "id-arena", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -2442,7 +2464,7 @@ dependencies = [ "id-arena", "indoc 2.0.5", "itertools 0.12.1", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "rust-analyzer-salsa", "smol_str 0.2.2", @@ -2461,7 +2483,7 @@ dependencies = [ "itertools 0.10.5", "lalrpop 0.19.12", "lalrpop-util 0.19.12", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "regex", "salsa", @@ -2483,7 +2505,7 @@ dependencies = [ "itertools 0.10.5", "lalrpop 0.19.12", "lalrpop-util 0.19.12", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "regex", "salsa", @@ -2506,7 +2528,7 @@ dependencies = [ "itertools 0.10.5", "lalrpop 0.19.12", "lalrpop-util 0.19.12", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "regex", "salsa", @@ -2530,7 +2552,7 @@ dependencies = [ "itertools 0.12.1", "lalrpop 0.20.2", "lalrpop-util 0.20.2", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "regex", @@ -2591,7 +2613,7 @@ dependencies = [ "cairo-lang-sierra-type-size", "cairo-lang-utils 2.8.4", "itertools 0.12.1", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "thiserror 1.0.65", ] @@ -2644,7 +2666,7 @@ dependencies = [ "cairo-lang-sierra-type-size", "cairo-lang-utils 2.8.4", "itertools 0.12.1", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "thiserror 1.0.65", ] @@ -2669,7 +2691,7 @@ dependencies = [ "id-arena", "indexmap 1.9.3", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "salsa", "smol_str 0.1.24", ] @@ -2694,7 +2716,7 @@ dependencies = [ "id-arena", "indexmap 1.9.3", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "salsa", "smol_str 0.2.2", ] @@ -2720,7 +2742,7 @@ dependencies = [ "id-arena", "indexmap 1.9.3", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "salsa", "smol_str 0.2.2", ] @@ -2766,7 +2788,7 @@ dependencies = [ "indoc 1.0.9", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "thiserror 1.0.65", ] @@ -2788,7 +2810,7 @@ dependencies = [ "indoc 2.0.5", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "thiserror 1.0.65", ] @@ -2811,7 +2833,7 @@ dependencies = [ "indoc 2.0.5", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "thiserror 1.0.65", ] @@ -2831,7 +2853,7 @@ dependencies = [ "cairo-lang-utils 2.8.4", "indoc 2.0.5", "itertools 0.12.1", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "starknet-types-core 0.1.7 (git+https://github.com/kasarlabs/types-rs.git?branch=feat-deserialize-v0.1.7)", "thiserror 1.0.65", @@ -2876,7 +2898,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "log", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -2915,7 +2937,7 @@ dependencies = [ "indoc 2.0.5", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "once_cell", @@ -2956,7 +2978,7 @@ dependencies = [ "indoc 2.0.5", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "once_cell", @@ -3009,7 +3031,7 @@ dependencies = [ "cairo-lang-utils 2.8.4", "convert_case 0.6.0", "itertools 0.12.1", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -3040,7 +3062,7 @@ dependencies = [ "cairo-lang-debug 1.0.0-rc0", "cairo-lang-filesystem 1.0.0-rc0", "cairo-lang-utils 1.0.0-rc0", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -3057,7 +3079,7 @@ dependencies = [ "cairo-lang-debug 1.1.1", "cairo-lang-filesystem 1.1.1", "cairo-lang-utils 1.1.1", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "salsa", "smol_str 0.2.2", @@ -3074,7 +3096,7 @@ dependencies = [ "cairo-lang-debug 2.8.4", "cairo-lang-filesystem 2.8.4", "cairo-lang-utils 2.8.4", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "rust-analyzer-salsa", "smol_str 0.2.2", @@ -3148,7 +3170,7 @@ dependencies = [ "indexmap 1.9.3", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -3163,7 +3185,7 @@ dependencies = [ "indexmap 1.9.3", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -3180,7 +3202,7 @@ dependencies = [ "indexmap 1.9.3", "itertools 0.10.5", "log", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -3196,7 +3218,7 @@ dependencies = [ "hashbrown 0.14.5", "indexmap 2.6.0", "itertools 0.12.1", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "parity-scale-codec", "schemars", @@ -3218,11 +3240,11 @@ dependencies = [ "keccak", "lazy_static", "nom", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-prime", "num-traits 0.2.19", - "rand", + "rand 0.8.5", "rust_decimal", "serde", "serde_json", @@ -3234,6 +3256,12 @@ dependencies = [ "zip", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.1.31" @@ -3275,6 +3303,33 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.4.4" @@ -3336,6 +3391,21 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.3" @@ -3456,6 +3526,42 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types 0.5.0", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.14" @@ -3474,6 +3580,42 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits 0.2.19", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -3512,7 +3654,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -3727,6 +3869,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -3737,6 +3888,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -3748,6 +3911,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + [[package]] name = "dotenv" version = "0.15.0" @@ -3766,6 +3938,18 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dwrote" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70182709525a3632b2ba96b6569225467b18ecb4a77f46d255f713a6bebf05fd" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -3805,7 +3989,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -3876,7 +4060,7 @@ dependencies = [ "hex", "hmac", "pbkdf2", - "rand", + "rand 0.8.5", "scrypt", "serde", "serde_json", @@ -3957,6 +4141,15 @@ dependencies = [ "bytes", ] +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fdlimit" version = "0.3.0" @@ -3973,7 +4166,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -3984,7 +4177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -4005,6 +4198,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + [[package]] name = "fnv" version = "1.0.7" @@ -4017,13 +4216,59 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +[[package]] +name = "font-kit" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64b34f4efd515f905952d91bc185039863705592c0c53ae6d979805dd154520" +dependencies = [ + "bitflags 2.6.0", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", ] [[package]] @@ -4032,6 +4277,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -4047,6 +4298,23 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "funty" version = "2.0.0" @@ -4218,6 +4486,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.31.1" @@ -4318,7 +4596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -4360,6 +4638,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -4760,6 +5048,20 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-traits 0.2.19", + "png", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -4810,7 +5112,7 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg", + "autocfg 1.4.0", "hashbrown 0.12.3", "serde", ] @@ -4930,6 +5232,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.72" @@ -4996,7 +5304,7 @@ dependencies = [ "jsonrpsee-types", "parking_lot 0.12.3", "pin-project", - "rand", + "rand 0.8.5", "rustc-hash 1.1.0", "serde", "serde_json", @@ -5317,7 +5625,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg", + "autocfg 1.4.0", "scopeguard", ] @@ -5400,7 +5708,7 @@ dependencies = [ "opentelemetry-semantic-conventions", "opentelemetry-stdout", "opentelemetry_sdk", - "rand", + "rand 0.8.5", "rayon", "reqwest 0.12.8", "serde", @@ -5476,6 +5784,7 @@ dependencies = [ "mc-analytics", "mc-db", "mp-block", + "mp-bloom-filter", "mp-chain-config", "mp-class", "mp-convert", @@ -5560,6 +5869,7 @@ dependencies = [ "librocksdb-sys", "mc-analytics", "mp-block", + "mp-bloom-filter", "mp-chain-config", "mp-class", "mp-receipt", @@ -5842,6 +6152,7 @@ dependencies = [ "mc-gateway-client", "mc-mempool", "mp-block", + "mp-bloom-filter", "mp-chain-config", "mp-class", "mp-convert", @@ -5956,6 +6267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -6020,6 +6332,24 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "mp-bloom-filter" +version = "0.7.0" +dependencies = [ + "ahash", + "bincode 1.3.3", + "criterion", + "mp-receipt", + "plotters", + "rand 0.8.5", + "rayon", + "serde", + "starknet-types-core 0.1.7 (git+https://github.com/kasarlabs/types-rs.git?branch=feat-deserialize-v0.1.7)", + "statistical", + "thiserror 2.0.3", + "twox-hash", +] + [[package]] name = "mp-chain-config" version = "0.7.0" @@ -6058,7 +6388,7 @@ dependencies = [ "flate2", "lazy_static", "mp-convert", - "num-bigint", + "num-bigint 0.4.6", "serde", "serde_json", "starknet-core", @@ -6166,7 +6496,7 @@ dependencies = [ "mp-chain-config", "mp-class", "mp-convert", - "num-bigint", + "num-bigint 0.4.6", "serde", "serde_json", "serde_with", @@ -6194,7 +6524,7 @@ dependencies = [ "opentelemetry-stdout", "opentelemetry_sdk", "paste", - "rand", + "rand 0.8.5", "rayon", "rstest 0.18.2", "serde", @@ -6276,6 +6606,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint 0.2.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.2.4", + "num-traits 0.2.19", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.4.0", + "num-integer", + "num-traits 0.2.19", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -6284,7 +6639,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits 0.2.19", - "rand", + "rand 0.8.5", "serde", ] @@ -6294,7 +6649,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ - "autocfg", + "autocfg 1.4.0", "num-traits 0.2.19", ] @@ -6313,13 +6668,24 @@ dependencies = [ "num-traits 0.2.19", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg 1.4.0", + "num-integer", + "num-traits 0.2.19", +] + [[package]] name = "num-modular" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", ] @@ -6333,11 +6699,23 @@ dependencies = [ "bitvec", "either", "lru", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-modular", "num-traits 0.2.19", - "rand", + "rand 0.8.5", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg 1.4.0", + "num-bigint 0.2.6", + "num-integer", + "num-traits 0.2.19", ] [[package]] @@ -6346,7 +6724,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -6367,7 +6745,7 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg", + "autocfg 1.4.0", "libm", ] @@ -6445,7 +6823,7 @@ checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -6574,13 +6952,19 @@ dependencies = [ "once_cell", "opentelemetry", "percent-encoding", - "rand", + "rand 0.8.5", "serde_json", "thiserror 1.0.65", "tokio", "tokio-stream", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "4.4.0" @@ -6683,7 +7067,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -6705,6 +7089,25 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf07ef4804cfa9aea3b04a7bbdd5a40031dbb6b4f2cbaf2b011666c80c5b4f2" +dependencies = [ + "rustc_version 0.4.1", +] + [[package]] name = "pbkdf2" version = "0.11.0" @@ -6761,7 +7164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared 0.11.2", - "rand", + "rand 0.8.5", ] [[package]] @@ -6860,6 +7263,65 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "chrono", + "font-kit", + "image", + "lazy_static", + "num-traits 0.2.19", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-bitmap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ce181e3f6bf82d6c1dc569103ca7b1bd964c60ba03d7e6cdfbb3e3eb7f7405" +dependencies = [ + "gif", + "image", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.7.3" @@ -6996,9 +7458,9 @@ dependencies = [ "bitflags 2.6.0", "lazy_static", "num-traits 0.2.19", - "rand", - "rand_chacha", - "rand_xorshift", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift 0.3.0", "regex-syntax 0.8.5", "rusty-fork", "tempfile", @@ -7069,6 +7531,25 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift 0.1.1", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -7076,11 +7557,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "serde", ] +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -7088,9 +7579,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", ] +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -7100,13 +7606,75 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -7135,6 +7703,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -7432,12 +8009,12 @@ dependencies = [ "ark-ff 0.4.2", "bytes", "fastrlp", - "num-bigint", + "num-bigint 0.4.6", "num-traits 0.2.19", "parity-scale-codec", "primitive-types", "proptest", - "rand", + "rand 0.8.5", "rlp", "ruint-macro", "serde", @@ -8071,9 +8648,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "similar" version = "2.6.0" @@ -8092,7 +8675,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg", + "autocfg 1.4.0", ] [[package]] @@ -8153,7 +8736,7 @@ dependencies = [ "http 0.2.12", "httparse", "log", - "rand", + "rand 0.8.5", "sha-1", ] @@ -8262,7 +8845,7 @@ dependencies = [ "crypto-bigint", "hex", "hmac", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "rfc6979", @@ -8282,7 +8865,7 @@ dependencies = [ "crypto-bigint", "hex", "hmac", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "rfc6979", @@ -8302,7 +8885,7 @@ dependencies = [ "crypto-bigint", "hex", "hmac", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "rfc6979", @@ -8320,7 +8903,7 @@ dependencies = [ "crypto-bigint", "hex", "hmac", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "rfc6979", @@ -8430,7 +9013,7 @@ dependencies = [ "crypto-bigint", "eth-keystore", "getrandom", - "rand", + "rand 0.8.5", "starknet-core", "starknet-crypto 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.65", @@ -8445,7 +9028,7 @@ dependencies = [ "lambdaworks-crypto", "lambdaworks-math", "lazy_static", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "parity-scale-codec", @@ -8459,7 +9042,7 @@ source = "git+https://github.com/jbcaron/types-rs.git?branch=fork#570eb3eb21d64a dependencies = [ "lambdaworks-crypto", "lambdaworks-math", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits 0.2.19", "serde", @@ -8504,6 +9087,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "statistical" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49d57902bb128e5e38b5218d3681215ae3e322d99f65d5420e9849730d2ea372" +dependencies = [ + "num", + "rand 0.6.5", +] + [[package]] name = "string_cache" version = "0.8.7" @@ -8867,6 +9460,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -9064,7 +9667,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -9234,6 +9837,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + [[package]] name = "tungstenite" version = "0.21.0" @@ -9246,13 +9855,22 @@ dependencies = [ "http 1.1.0", "httparse", "log", - "rand", + "rand 0.8.5", "sha1", "thiserror 1.0.65", "url", "utf-8", ] +[[package]] +name = "twox-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7b17f197b3050ba473acf9181f7b1d3b66d1cf7356c6cc57886662276e65908" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "typenum" version = "1.17.0" @@ -9537,6 +10155,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "winapi" version = "0.3.9" @@ -9784,6 +10408,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "wyz" version = "0.5.1" @@ -9814,6 +10447,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yeslogic-fontconfig-sys" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" +dependencies = [ + "dlib", + "once_cell", + "pkg-config", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 1aa14a013..575dcadce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "crates/madara/client/block_import", "crates/madara/node", "crates/madara/primitives/block", + "crates/madara/primitives/bloom_filter", "crates/madara/primitives/convert", "crates/madara/primitives/transactions", "crates/madara/primitives/class", @@ -46,6 +47,7 @@ default-members = [ "crates/madara/client/analytics", "crates/madara/node", "crates/madara/primitives/block", + "crates/madara/primitives/bloom_filter", "crates/madara/primitives/convert", "crates/madara/primitives/transactions", "crates/madara/primitives/class", @@ -110,6 +112,7 @@ m-proc-macros = { path = "crates/madara/proc-macros", default-features = false } # Madara primtitives mp-block = { path = "crates/madara/primitives/block", default-features = false } +mp-bloom-filter = { path = "crates/madara/primitives/bloom_filter", default-features = false } mp-convert = { path = "crates/madara/primitives/convert", default-features = false } mp-transactions = { path = "crates/madara/primitives/transactions", default-features = false } mp-class = { path = "crates/madara/primitives/class", default-features = false } @@ -167,6 +170,7 @@ alloy = { version = "0.4.0", features = [ # Other third party dependencies paste = "1.0.15" anyhow = "1.0" +ahash = "0.8" bigdecimal = "0.4.5" assert_matches = "1.5" async-trait = "0.1" diff --git a/crates/madara/client/block_import/Cargo.toml b/crates/madara/client/block_import/Cargo.toml index 13cf872d5..12ae9b897 100644 --- a/crates/madara/client/block_import/Cargo.toml +++ b/crates/madara/client/block_import/Cargo.toml @@ -32,6 +32,7 @@ tokio.workspace = true mc-analytics.workspace = true mc-db.workspace = true mp-block.workspace = true +mp-bloom-filter.workspace = true mp-chain-config.workspace = true mp-class.workspace = true mp-convert.workspace = true diff --git a/crates/madara/client/block_import/src/verify_apply.rs b/crates/madara/client/block_import/src/verify_apply.rs index f830d23ab..89e29cf0b 100644 --- a/crates/madara/client/block_import/src/verify_apply.rs +++ b/crates/madara/client/block_import/src/verify_apply.rs @@ -9,6 +9,7 @@ use mp_block::{ header::PendingHeader, BlockId, Header, MadaraBlockInfo, MadaraBlockInner, MadaraMaybePendingBlock, MadaraMaybePendingBlockInfo, MadaraPendingBlockInfo, }; +use mp_bloom_filter::EventBloomWriter; use mp_convert::{FeltHexDisplay, ToFelt}; use starknet_api::core::ChainId; use starknet_types_core::felt::Felt; @@ -82,6 +83,16 @@ pub fn verify_apply_inner( // Update contract and its storage tries let global_state_root = update_tries(backend, &block, &validation, block_number)?; + // Event bloom filter + let event_bloom = { + let mut events_iter = block.receipts.iter().flat_map(|tx| tx.events().iter()).peekable(); + if events_iter.peek().is_none() { + None + } else { + Some(EventBloomWriter::from_events(events_iter)) + } + }; + // Block hash let (block_hash, header) = block_hash(&block, &validation, block_number, parent_block_hash, global_state_root)?; @@ -101,6 +112,7 @@ pub fn verify_apply_inner( }, block.state_diff, block.converted_classes, + event_bloom, block.visited_segments, None, ) @@ -146,6 +158,7 @@ pub fn verify_apply_pending_inner( }, block.state_diff, block.converted_classes, + None, block.visited_segments, None, ) @@ -411,7 +424,7 @@ mod verify_apply_tests { if populate_db { let header = create_dummy_header(); let pending_block = finalized_block_zero(header); - backend.store_block(pending_block.clone(), finalized_state_diff_zero(), vec![], None, None).unwrap(); + backend.store_block(pending_block.clone(), finalized_state_diff_zero(), vec![], None, None, None).unwrap(); } // Create a validation context with the specified ignore_block_order flag @@ -665,7 +678,7 @@ mod verify_apply_tests { let mut header = create_dummy_header(); header.block_number = 0; let pending_block = finalized_block_zero(header); - backend.store_block(pending_block.clone(), finalized_state_diff_zero(), vec![], None, None).unwrap(); + backend.store_block(pending_block.clone(), finalized_state_diff_zero(), vec![], None, None, None).unwrap(); assert_eq!(backend.get_latest_block_n().unwrap(), Some(0)); @@ -691,7 +704,7 @@ mod verify_apply_tests { let mut header = create_dummy_header(); header.block_number = 0; let pending_block = finalized_block_zero(header); - backend.store_block(pending_block.clone(), finalized_state_diff_zero(), vec![], None, None).unwrap(); + backend.store_block(pending_block.clone(), finalized_state_diff_zero(), vec![], None, None, None).unwrap(); assert_eq!(backend.get_latest_block_n().unwrap(), Some(0)); @@ -727,7 +740,7 @@ mod verify_apply_tests { let mut genesis_header = create_dummy_header(); genesis_header.block_number = 0; let genesis_block = finalized_block_zero(genesis_header.clone()); - backend.store_block(genesis_block, finalized_state_diff_zero(), vec![], None, None).unwrap(); + backend.store_block(genesis_block, finalized_state_diff_zero(), vec![], None, None, None).unwrap(); assert_eq!(backend.get_latest_block_n().unwrap(), Some(0)); @@ -773,7 +786,7 @@ mod verify_apply_tests { let mut genesis_header = create_dummy_header(); genesis_header.block_number = 0; let genesis_block = finalized_block_zero(genesis_header.clone()); - backend.store_block(genesis_block, finalized_state_diff_zero(), vec![], None, None).unwrap(); + backend.store_block(genesis_block, finalized_state_diff_zero(), vec![], None, None, None).unwrap(); assert_eq!(backend.get_latest_block_n().unwrap(), Some(0)); diff --git a/crates/madara/client/block_production/src/lib.rs b/crates/madara/client/block_production/src/lib.rs index 131620c9b..d8866d6b6 100644 --- a/crates/madara/client/block_production/src/lib.rs +++ b/crates/madara/client/block_production/src/lib.rs @@ -451,6 +451,7 @@ impl BlockProductionTask { self.block.clone().into(), new_state_diff, self.declared_classes.clone(), + None, Some(visited_segments), Some(bouncer_weights), )?; diff --git a/crates/madara/client/db/Cargo.toml b/crates/madara/client/db/Cargo.toml index 0f9c30e2d..e93e0b0ae 100644 --- a/crates/madara/client/db/Cargo.toml +++ b/crates/madara/client/db/Cargo.toml @@ -21,6 +21,7 @@ targets = ["x86_64-unknown-linux-gnu"] # Madara mc-analytics = { workspace = true } mp-block = { workspace = true } +mp-bloom-filter = { workspace = true } mp-chain-config = { workspace = true } mp-class = { workspace = true } mp-receipt = { workspace = true } diff --git a/crates/madara/client/db/src/block_db.rs b/crates/madara/client/db/src/block_db.rs index 17480ca96..cdd2d9ce4 100644 --- a/crates/madara/client/db/src/block_db.rs +++ b/crates/madara/client/db/src/block_db.rs @@ -8,8 +8,9 @@ use mp_block::{ BlockId, BlockTag, MadaraBlock, MadaraBlockInfo, MadaraBlockInner, MadaraMaybePendingBlock, MadaraMaybePendingBlockInfo, MadaraPendingBlock, MadaraPendingBlockInfo, VisitedSegments, }; +use mp_bloom_filter::{EventBloomReader, EventBloomWriter}; use mp_state_update::StateDiff; -use rocksdb::WriteOptions; +use rocksdb::{Direction, IteratorMode, WriteOptions}; use starknet_api::core::ChainId; use starknet_types_core::felt::Felt; use starknet_types_rpc::EmittedEvent; @@ -285,7 +286,12 @@ impl MadaraBackend { /// Also clears pending block #[tracing::instrument(skip(self), fields(module = "BlockDB"))] - pub(crate) fn block_db_store_block(&self, block: &MadaraBlock, state_diff: &StateDiff) -> Result<()> { + pub(crate) fn block_db_store_block( + &self, + block: &MadaraBlock, + state_diff: &StateDiff, + events_bloom: Option, + ) -> Result<()> { let mut tx = WriteBatchWithTransaction::default(); let tx_hash_to_block_n = self.db.get_column(Column::TxHashToBlockN); @@ -293,6 +299,7 @@ impl MadaraBackend { let block_n_to_block = self.db.get_column(Column::BlockNToBlockInfo); let block_n_to_block_inner = self.db.get_column(Column::BlockNToBlockInner); let block_n_to_state_diff = self.db.get_column(Column::BlockNToStateDiff); + let event_bloom = self.db.get_column(Column::EventBloom); let meta = self.db.get_column(Column::BlockStorageMeta); let block_hash_encoded = bincode::serialize(&block.info.block_hash)?; @@ -306,6 +313,9 @@ impl MadaraBackend { tx.put_cf(&block_hash_to_block_n, block_hash_encoded, &block_n_encoded); tx.put_cf(&block_n_to_block_inner, &block_n_encoded, bincode::serialize(&block.inner)?); tx.put_cf(&block_n_to_state_diff, &block_n_encoded, bincode::serialize(state_diff)?); + if let Some(events_bloom) = events_bloom { + tx.put_cf(&event_bloom, &block_n_encoded, bincode::serialize(&events_bloom)?); + } tx.put_cf(&meta, ROW_SYNC_TIP, block_n_encoded); // susbcribers @@ -480,4 +490,23 @@ impl MadaraBackend { } } } + + #[tracing::instrument(skip(self), fields(module = "BlockDB"))] + pub fn get_event_filter_stream( + &self, + block_n: u64, + ) -> Result> + '_> { + let col = self.db.get_column(Column::EventBloom); + let key = bincode::serialize(&block_n)?; + let iter_mode = IteratorMode::From(&key, Direction::Forward); + let iter = self.db.iterator_cf(&col, iter_mode); + + Ok(iter.map(|kvs| { + kvs.map_err(MadaraStorageError::from).and_then(|(key, value)| { + let stored_block_n: u64 = bincode::deserialize(&key).map_err(MadaraStorageError::from)?; + let bloom = bincode::deserialize(&value).map_err(MadaraStorageError::from)?; + Ok((stored_block_n, bloom)) + }) + })) + } } diff --git a/crates/madara/client/db/src/lib.rs b/crates/madara/client/db/src/lib.rs index 33bc43c4e..9c773916d 100644 --- a/crates/madara/client/db/src/lib.rs +++ b/crates/madara/client/db/src/lib.rs @@ -108,6 +108,8 @@ pub enum Column { BlockHashToBlockN, /// One To One BlockNToStateDiff, + /// block_n => bloom filter for events + EventBloom, /// Meta column for block storage (sync tip, pending block) BlockStorageMeta, @@ -178,6 +180,7 @@ impl Column { BlockHashToBlockN, BlockStorageMeta, BlockNToStateDiff, + EventBloom, ClassInfo, ClassCompiled, PendingClassInfo, @@ -214,6 +217,7 @@ impl Column { BlockHashToBlockN => "block_hash_to_block_n", BlockStorageMeta => "block_storage_meta", BlockNToStateDiff => "block_n_to_state_diff", + EventBloom => "event_bloom", BonsaiContractsTrie => "bonsai_contracts_trie", BonsaiContractsFlat => "bonsai_contracts_flat", BonsaiContractsLog => "bonsai_contracts_log", @@ -241,6 +245,12 @@ impl Column { } } +#[cfg(test)] +#[test] +fn test_column_all() { + assert_eq!(Column::ALL.len(), Column::NUM_COLUMNS); +} + pub trait DatabaseExt { fn get_column(&self, col: Column) -> Arc>; } diff --git a/crates/madara/client/db/src/storage_updates.rs b/crates/madara/client/db/src/storage_updates.rs index b5f5ae59e..283be7347 100644 --- a/crates/madara/client/db/src/storage_updates.rs +++ b/crates/madara/client/db/src/storage_updates.rs @@ -4,6 +4,7 @@ use crate::MadaraStorageError; use blockifier::bouncer::BouncerWeights; use mp_block::VisitedSegments; use mp_block::{MadaraBlock, MadaraMaybePendingBlock, MadaraMaybePendingBlockInfo, MadaraPendingBlock}; +use mp_bloom_filter::EventBloomWriter; use mp_class::ConvertedClass; use mp_state_update::{ ContractStorageDiffItem, DeployedContractItem, NonceUpdate, ReplacedClassItem, StateDiff, StorageEntry, @@ -18,6 +19,7 @@ impl MadaraBackend { block: MadaraMaybePendingBlock, state_diff: StateDiff, converted_classes: Vec, + events_bloom: Option, visited_segments: Option, bouncer_weights: Option, ) -> Result<(), MadaraStorageError> { @@ -35,7 +37,7 @@ impl MadaraBackend { bouncer_weights, ), MadaraMaybePendingBlockInfo::NotPending(info) => { - self.block_db_store_block(&MadaraBlock { info, inner: block.inner }, &state_diff_cpy) + self.block_db_store_block(&MadaraBlock { info, inner: block.inner }, &state_diff_cpy, events_bloom) } }; diff --git a/crates/madara/client/db/src/tests/test_block.rs b/crates/madara/client/db/src/tests/test_block.rs index 2d7ed0c88..66f58d45f 100644 --- a/crates/madara/client/db/src/tests/test_block.rs +++ b/crates/madara/client/db/src/tests/test_block.rs @@ -24,8 +24,8 @@ mod block_tests { let block_hash = block.info.block_hash().unwrap(); let state_diff = finalized_state_diff_zero(); - backend.store_block(block.clone(), state_diff.clone(), vec![], None, None).unwrap(); - backend.store_block(pending_block_one(), pending_state_diff_one(), vec![], None, None).unwrap(); + backend.store_block(block.clone(), state_diff.clone(), vec![], None, None, None).unwrap(); + backend.store_block(pending_block_one(), pending_state_diff_one(), vec![], None, None, None).unwrap(); assert_eq!(backend.resolve_block_id(&BlockId::Hash(block_hash)).unwrap().unwrap(), DbBlockId::Number(0)); assert_eq!(backend.resolve_block_id(&BlockId::Number(0)).unwrap().unwrap(), DbBlockId::Number(0)); @@ -52,7 +52,7 @@ mod block_tests { let block = finalized_block_zero(Header::default()); let state_diff = finalized_state_diff_zero(); - backend.store_block(block.clone(), state_diff.clone(), vec![], None, None).unwrap(); + backend.store_block(block.clone(), state_diff.clone(), vec![], None, None, None).unwrap(); assert_eq!(backend.get_block_hash(&BLOCK_ID_0).unwrap().unwrap(), block.info.block_hash().unwrap()); assert_eq!(BLOCK_ID_0.resolve_db_block_id(backend).unwrap().unwrap(), BLOCK_ID_0); @@ -75,7 +75,7 @@ mod block_tests { let block = pending_block_one(); let state_diff = pending_state_diff_one(); - backend.store_block(block.clone(), state_diff.clone(), vec![], None, None).unwrap(); + backend.store_block(block.clone(), state_diff.clone(), vec![], None, None, None).unwrap(); assert!(backend.get_block_hash(&BLOCK_ID_PENDING).unwrap().is_none()); assert_eq!(backend.get_block_info(&BLOCK_ID_PENDING).unwrap().unwrap(), block.info); @@ -92,9 +92,9 @@ mod block_tests { let backend = db.backend(); backend - .store_block(finalized_block_zero(Header::default()), finalized_state_diff_zero(), vec![], None, None) + .store_block(finalized_block_zero(Header::default()), finalized_state_diff_zero(), vec![], None, None, None) .unwrap(); - backend.store_block(pending_block_one(), pending_state_diff_one(), vec![], None, None).unwrap(); + backend.store_block(pending_block_one(), pending_state_diff_one(), vec![], None, None, None).unwrap(); backend.clear_pending_block().unwrap(); assert!(backend.get_block(&BLOCK_ID_PENDING).unwrap().unwrap().inner.transactions.is_empty()); @@ -104,11 +104,11 @@ mod block_tests { "fake pending block parent hash must match with latest block in db" ); - backend.store_block(finalized_block_one(), finalized_state_diff_one(), vec![], None, None).unwrap(); + backend.store_block(finalized_block_one(), finalized_state_diff_one(), vec![], None, None, None).unwrap(); let block_pending = pending_block_two(); let state_diff = pending_state_diff_two(); - backend.store_block(block_pending.clone(), state_diff.clone(), vec![], None, None).unwrap(); + backend.store_block(block_pending.clone(), state_diff.clone(), vec![], None, None, None).unwrap(); assert!(backend.get_block_hash(&BLOCK_ID_PENDING).unwrap().is_none()); assert_eq!(backend.get_block_info(&BLOCK_ID_PENDING).unwrap().unwrap(), block_pending.info); @@ -123,11 +123,11 @@ mod block_tests { let backend = db.backend(); backend - .store_block(finalized_block_zero(Header::default()), finalized_state_diff_zero(), vec![], None, None) + .store_block(finalized_block_zero(Header::default()), finalized_state_diff_zero(), vec![], None, None, None) .unwrap(); let latest_block = finalized_block_one(); - backend.store_block(latest_block.clone(), finalized_state_diff_one(), vec![], None, None).unwrap(); + backend.store_block(latest_block.clone(), finalized_state_diff_one(), vec![], None, None, None).unwrap(); assert_eq!(backend.get_latest_block_n().unwrap().unwrap(), 1); } @@ -152,7 +152,7 @@ mod block_tests { let block = finalized_block_zero(Header::default()); let state_diff = finalized_state_diff_zero(); - backend.store_block(block.clone(), state_diff.clone(), vec![], None, None).unwrap(); + backend.store_block(block.clone(), state_diff.clone(), vec![], None, None, None).unwrap(); let tx_hash_1 = block.info.tx_hashes()[1]; assert_eq!(backend.find_tx_hash_block_info(&tx_hash_1).unwrap().unwrap(), (block.info.clone(), TxIndex(1))); @@ -165,11 +165,11 @@ mod block_tests { let backend = db.backend(); backend - .store_block(finalized_block_zero(Header::default()), finalized_state_diff_zero(), vec![], None, None) + .store_block(finalized_block_zero(Header::default()), finalized_state_diff_zero(), vec![], None, None, None) .unwrap(); let block_pending = pending_block_one(); - backend.store_block(block_pending.clone(), pending_state_diff_one(), vec![], None, None).unwrap(); + backend.store_block(block_pending.clone(), pending_state_diff_one(), vec![], None, None, None).unwrap(); let tx_hash_1 = block_pending.info.tx_hashes()[1]; assert_eq!( diff --git a/crates/madara/client/mempool/src/lib.rs b/crates/madara/client/mempool/src/lib.rs index 91eb6fc27..e6c4fa082 100644 --- a/crates/madara/client/mempool/src/lib.rs +++ b/crates/madara/client/mempool/src/lib.rs @@ -1681,6 +1681,7 @@ mod test { vec![], None, None, + None, ) .expect("Failed to store block"); @@ -1722,6 +1723,7 @@ mod test { vec![], None, None, + None, ) .expect("Failed to store block"); diff --git a/crates/madara/client/rpc/Cargo.toml b/crates/madara/client/rpc/Cargo.toml index 45f1e4280..d6101c5b9 100644 --- a/crates/madara/client/rpc/Cargo.toml +++ b/crates/madara/client/rpc/Cargo.toml @@ -29,6 +29,7 @@ mc-exec = { workspace = true } mc-gateway-client = { workspace = true } mc-mempool = { workspace = true } mp-block = { workspace = true, default-features = true } +mp-bloom-filter = { workspace = true } mp-chain-config = { workspace = true } mp-class = { workspace = true } mp-convert = { workspace = true, default-features = true } diff --git a/crates/madara/client/rpc/src/test_utils.rs b/crates/madara/client/rpc/src/test_utils.rs index 79a786e08..132ec2ef4 100644 --- a/crates/madara/client/rpc/src/test_utils.rs +++ b/crates/madara/client/rpc/src/test_utils.rs @@ -245,6 +245,7 @@ pub fn make_sample_chain_for_block_getters(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); @@ -270,6 +271,7 @@ pub fn make_sample_chain_for_block_getters(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); @@ -339,6 +341,7 @@ pub fn make_sample_chain_for_block_getters(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); @@ -377,6 +380,7 @@ pub fn make_sample_chain_for_block_getters(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); } @@ -560,6 +564,7 @@ pub fn make_sample_chain_for_state_updates(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); @@ -584,6 +589,7 @@ pub fn make_sample_chain_for_state_updates(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); @@ -608,6 +614,7 @@ pub fn make_sample_chain_for_state_updates(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); @@ -629,6 +636,7 @@ pub fn make_sample_chain_for_state_updates(backend: &MadaraBackend) -> SampleCha vec![], None, None, + None, ) .unwrap(); } diff --git a/crates/madara/client/rpc/src/utils/mod.rs b/crates/madara/client/rpc/src/utils/mod.rs index 8878be332..97c8b387f 100644 --- a/crates/madara/client/rpc/src/utils/mod.rs +++ b/crates/madara/client/rpc/src/utils/mod.rs @@ -190,6 +190,11 @@ mod tests { vec![vec![Felt::from_hex_unchecked("0x1")], vec![Felt::from_hex_unchecked("0x2")]] } + #[fixture] + fn matching_keys_empty() -> Vec> { + vec![vec![], vec![]] + } + #[fixture] fn non_matching_keys() -> Vec> { vec![vec![Felt::from_hex_unchecked("0x1")], vec![Felt::from_hex_unchecked("0x3")]] @@ -200,6 +205,15 @@ mod tests { assert!(event_match_filter(&base_event, Some(&matching_address), Some(&matching_keys))); } + #[rstest] + fn test_address_and_empty_keys_match( + base_event: Event, + matching_address: Felt, + matching_keys_empty: Vec>, + ) { + assert!(event_match_filter(&base_event, Some(&matching_address), Some(&matching_keys_empty))); + } + #[rstest] fn test_address_does_not_match(base_event: Event, non_matching_address: Felt, matching_keys: Vec>) { assert!(!event_match_filter(&base_event, Some(&non_matching_address), Some(&matching_keys))); diff --git a/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/block_hash_and_number.rs b/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/block_hash_and_number.rs index 43c173051..e275be5e4 100644 --- a/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/block_hash_and_number.rs +++ b/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/block_hash_and_number.rs @@ -55,6 +55,7 @@ mod tests { vec![], None, None, + None, ) .unwrap(); @@ -74,6 +75,7 @@ mod tests { vec![], None, None, + None, ) .unwrap(); @@ -96,6 +98,7 @@ mod tests { vec![], None, None, + None, ) .unwrap(); @@ -125,6 +128,7 @@ mod tests { vec![], None, None, + None, ) .unwrap(); diff --git a/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_block_with_receipts.rs b/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_block_with_receipts.rs index 3438dca3a..d070f013b 100644 --- a/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_block_with_receipts.rs +++ b/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_block_with_receipts.rs @@ -253,6 +253,7 @@ mod tests { vec![], None, None, + None, ) .unwrap(); diff --git a/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_events.rs b/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_events.rs index 30a560e6d..6310add5b 100644 --- a/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_events.rs +++ b/crates/madara/client/rpc/src/versions/user/v0_7_1/methods/read/get_events.rs @@ -1,4 +1,5 @@ use mp_block::{BlockId, BlockTag, MadaraMaybePendingBlock, MadaraMaybePendingBlockInfo}; +use mp_bloom_filter::EventBloomSearcher; use starknet_types_core::felt::Felt; use starknet_types_rpc::{EmittedEvent, Event, EventContent, EventFilterWithPageRequest, EventsChunk}; @@ -6,6 +7,7 @@ use crate::constants::{MAX_EVENTS_CHUNK_SIZE, MAX_EVENTS_KEYS}; use crate::errors::{StarknetRpcApiError, StarknetRpcResult}; use crate::types::ContinuationToken; use crate::utils::event_match_filter; +use crate::utils::ResultExt; use crate::Starknet; /// Returns all events matching the given filter. @@ -32,6 +34,8 @@ pub async fn get_events( starknet: &Starknet, filter: EventFilterWithPageRequest, ) -> StarknetRpcResult> { + // TODO: use a continuation token like BlockN_EventN were EventN is the index of the event in the block and not the index of filtered events + let from_address = filter.address; let keys = filter.keys; let chunk_size = filter.chunk_size; @@ -61,13 +65,28 @@ pub async fn get_events( let from_block = continuation_token.block_n; let mut filtered_events: Vec> = Vec::new(); - for current_block in from_block..=to_block { - let (_pending, block) = if current_block <= latest_block { - (false, starknet.get_block(&BlockId::Number(current_block))?) - } else { - (true, starknet.get_block(&BlockId::Tag(BlockTag::Pending))?) - }; + let iter_filter = starknet + .backend + .get_event_filter_stream(from_block) + .or_internal_server_error("Error getting event filter stream")?; + + let key_filter = EventBloomSearcher::new(from_address.as_ref(), keys.as_deref()); + + for filter_block in iter_filter { + let (current_block, filter) = filter_block.or_internal_server_error("Error getting next filter block")?; + + if current_block > latest_block { + break; + } + + if !key_filter.search(&filter) { + continue; + } + + let block = + starknet.get_block(&BlockId::Number(current_block)).or_internal_server_error("Error getting block")?; + // TODO: take only the events to fill the chunk not all the events let block_filtered_events: Vec> = drain_block_events(block) .filter(|event| event_match_filter(&event.event, from_address.as_ref(), keys.as_deref())) .collect(); @@ -94,7 +113,12 @@ pub async fn get_events( return Ok(EventsChunk { events: filtered_events, continuation_token: token }); } + if current_block == to_block { + break; + } } + // TODO: handle the case where 'to_block' is pending + Ok(EventsChunk { events: filtered_events, continuation_token: None }) } diff --git a/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_events.rs b/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_events.rs index 77c4897d9..2fb8f3b4f 100644 --- a/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_events.rs +++ b/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_events.rs @@ -151,6 +151,7 @@ mod test { vec![], None, None, + None, ) .expect("Storing block"); diff --git a/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_new_heads.rs b/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_new_heads.rs index 742981c66..05ea2b213 100644 --- a/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_new_heads.rs +++ b/crates/madara/client/rpc/src/versions/user/v0_8_0/methods/ws/subscribe_new_heads.rs @@ -164,6 +164,7 @@ mod test { vec![], None, None, + None, ) .expect("Storing block"); diff --git a/crates/madara/primitives/bloom_filter/Cargo.toml b/crates/madara/primitives/bloom_filter/Cargo.toml new file mode 100644 index 000000000..10530e46a --- /dev/null +++ b/crates/madara/primitives/bloom_filter/Cargo.toml @@ -0,0 +1,43 @@ +[package] +description = "Madara primitive for block state update" +name = "mp-bloom-filter" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true +homepage.workspace = true + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +ahash.workspace = true +mp-receipt.workspace = true +serde.workspace = true +starknet-types-core.workspace = true +thiserror.workspace = true + +[dev-dependencies] +criterion = { version = "0.5", features = ["html_reports"] } +twox-hash = "2.1" + +rand = "0.8" +plotters = "0.3" +statistical = "1.0" +rayon.workspace = true +bincode.workspace = true + +[profile.bench] +opt-level = 3 +lto = "thin" +codegen-units = 1 +debug = true + +[[bench]] +name = "bloom_benchmark" +harness = false +path = "benches/bloom_benchmark.rs" diff --git a/crates/madara/primitives/bloom_filter/benches/bloom_benchmark.rs b/crates/madara/primitives/bloom_filter/benches/bloom_benchmark.rs new file mode 100644 index 000000000..100ea7584 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/benches/bloom_benchmark.rs @@ -0,0 +1,192 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use rand::rngs::StdRng; +use rand::{Rng, SeedableRng}; +use rayon::prelude::*; + +use mp_bloom_filter::{AtomicBitStore, BitStore, BloomFilter}; + +use ahash::AHasher; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use twox_hash::XxHash64; + +/// Configuration constants for benchmarks +const KEY_SIZE: usize = 32; +const HASH_COUNT: u8 = 7; +const BITS_PER_ELEMENT: f64 = 9.6; +const FALSE_POSITIF_RATE: f64 = 0.01; + +const TEST_DATA_SIZE: usize = 100_000; +const LOOKUP_FILL_RATIO: f64 = 0.5; +const SAMPLE_SIZE: usize = 50; +const MEASUREMENT_TIME: u64 = 5; + +/// Type alias for test data +type TestData = [u8; KEY_SIZE]; + +type BenchFn = for<'a, 'b, 'c> fn(&'a mut Criterion, &'b [TestData], &'c str); + +struct HasherBenchmarks { + name: &'static str, + sequential_insertion: BenchFn, + parallel_insertion: BenchFn, + lookup: BenchFn, + parallel_lookup: BenchFn, +} + +fn get_hasher_benchmarks(name: &'static str) -> HasherBenchmarks { + HasherBenchmarks { + name, + sequential_insertion: bench_sequential_insertion:: as BenchFn, + parallel_insertion: bench_parallel_insertion:: as BenchFn, + lookup: bench_lookup:: as BenchFn, + parallel_lookup: bench_parallel_lookup:: as BenchFn, + } +} + +/// Generate test data for benchmarking +fn generate_test_data(size: usize) -> Vec { + let mut rng = StdRng::seed_from_u64(42); + (0..size) + .map(|_| { + let mut key = [0u8; KEY_SIZE]; + rng.fill(&mut key); + key + }) + .collect() +} + +/// Create a pre-filled atomic filter for mutable lookups +fn create_atomic_filter(test_data: &[TestData]) -> BloomFilter { + let size = (test_data.len() as f64 * BITS_PER_ELEMENT).ceil() as u64; + let filter = BloomFilter::::new(size, HASH_COUNT); + + let items_to_insert = (test_data.len() as f64 * LOOKUP_FILL_RATIO) as usize; + for item in test_data.iter().take(items_to_insert) { + filter.add(item); + } + filter +} + +/// Create a pre-filled readonly filter +fn create_readonly_filter(test_data: &[TestData]) -> BloomFilter { + create_atomic_filter::(test_data).finalize() +} + +/// Benchmark sequential insertion +fn bench_sequential_insertion(c: &mut Criterion, test_data: &[TestData], name: &str) { + let size = (test_data.len() as f64 * BITS_PER_ELEMENT).ceil() as u64; + + let mut group = c.benchmark_group(format!("Sequential Insertion/{}", name)); + group.sample_size(SAMPLE_SIZE); + group.measurement_time(std::time::Duration::from_secs(MEASUREMENT_TIME)); + + group.bench_function("atomic", |b| { + b.iter(|| { + let filter = BloomFilter::::new(size, HASH_COUNT); + for item in test_data { + filter.add(black_box(item)); + } + }); + }); + + // group.bench_function("non atomic", |b| { + // b.iter(|| { + // let mut filter = BloomFilter::::optimal(TEST_DATA_SIZE, FALSE_POSITIVE_RATE) + // .expect("Failed to create optimal filter"); + // for item in test_data { + // filter.add(black_box(item)); + // } + // }); + // }); + + group.finish(); +} + +/// Benchmark parallel insertion using rayon +fn bench_parallel_insertion(c: &mut Criterion, test_data: &[TestData], name: &str) { + let size = (test_data.len() as f64 * BITS_PER_ELEMENT).ceil() as u64; + + let mut group = c.benchmark_group(format!("Parallel Insertion/{}", name)); + group.sample_size(SAMPLE_SIZE); + group.measurement_time(std::time::Duration::from_secs(MEASUREMENT_TIME)); + + group.bench_function("atomic", |b| { + b.iter(|| { + let filter = BloomFilter::::new(size, HASH_COUNT); + test_data.par_iter().for_each(|item| { + filter.add(black_box(item)); + }); + }); + }); + + group.finish(); +} + +/// Benchmark lookups +fn bench_lookup(c: &mut Criterion, test_data: &[TestData], name: &str) { + let mut group = c.benchmark_group(format!("Lookup/{}", name)); + group.sample_size(SAMPLE_SIZE); + group.measurement_time(std::time::Duration::from_secs(MEASUREMENT_TIME)); + + let readonly_filter = create_readonly_filter::(test_data); + + // Benchmark readonly filter lookups + group.bench_function("non atomic", |b| { + b.iter(|| { + for item in test_data { + black_box(readonly_filter.might_contain(item)); + } + }); + }); + + group.finish(); +} + +/// Benchmark parallel lookups using rayon +fn bench_parallel_lookup(c: &mut Criterion, test_data: &[TestData], name: &str) { + let mut group = c.benchmark_group(format!("Parallel Lookup/{}", name)); + group.sample_size(SAMPLE_SIZE); + group.measurement_time(std::time::Duration::from_secs(MEASUREMENT_TIME)); + + let readonly_filter = create_readonly_filter::(test_data); + + // Benchmark parallel readonly filter lookups + group.bench_function("non atomic", |b| { + b.iter(|| { + test_data.par_iter().for_each(|item| { + black_box(readonly_filter.might_contain(item)); + }); + }); + }); + + group.finish(); +} + +fn bench_bloom_filters(c: &mut Criterion) { + let test_data = generate_test_data(TEST_DATA_SIZE); + + // Test different hash functions + let hashers = [ + get_hasher_benchmarks::("DefaultHasher"), + get_hasher_benchmarks::("AHasher"), + get_hasher_benchmarks::("XxHash64"), + ]; + + for hasher in &hashers { + (hasher.sequential_insertion)(c, &test_data, hasher.name); + (hasher.parallel_insertion)(c, &test_data, hasher.name); + (hasher.lookup)(c, &test_data, hasher.name); + (hasher.parallel_lookup)(c, &test_data, hasher.name); + } +} + +criterion_group! { + name = benches; + config = Criterion::default() + .with_plots() + .sample_size(SAMPLE_SIZE); + targets = bench_bloom_filters +} + +criterion_main!(benches); diff --git a/crates/madara/primitives/bloom_filter/src/constants.rs b/crates/madara/primitives/bloom_filter/src/constants.rs new file mode 100644 index 000000000..56d2be1d5 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/constants.rs @@ -0,0 +1,6 @@ +pub const MIN_FILTER_SIZE: usize = 64; +pub const MAX_FILTER_SIZE: usize = 1 << 24; // 16 MiB +pub const MIN_HASH_COUNT: usize = 1; +pub const MAX_HASH_COUNT: usize = 20; +pub const DEFAULT_SIZE: usize = 1024; +pub const DEFAULT_HASH_COUNT: usize = 3; diff --git a/crates/madara/primitives/bloom_filter/src/error.rs b/crates/madara/primitives/bloom_filter/src/error.rs new file mode 100644 index 000000000..5cb12b38b --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/error.rs @@ -0,0 +1,11 @@ +#[derive(Debug, thiserror::Error)] +pub enum BloomError { + #[error("Filter size {0} is too small (minimum: {1})")] + SizeTooSmall(usize, usize), + #[error("Filter size {0} is too large (maximum: {1})")] + SizeTooLarge(usize, usize), + #[error("Hash count {0} is too small (minimum: {1})")] + TooFewHashes(usize, usize), + #[error("Hash count {0} is too large (maximum: {1})")] + TooManyHashes(usize, usize), +} diff --git a/crates/madara/primitives/bloom_filter/src/events.rs b/crates/madara/primitives/bloom_filter/src/events.rs new file mode 100644 index 000000000..63b6927f5 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/events.rs @@ -0,0 +1,143 @@ +use ahash::AHasher; +use mp_receipt::Event; +use serde::{Deserialize, Deserializer, Serialize}; +use starknet_types_core::felt::Felt; +use std::{collections::HashSet, fmt}; + +use crate::{AtomicBitStore, BitStore, BloomFilter, PreCalculatedHashes}; + +/// Number of hash functions used in the Bloom filter. +/// The value 7 is optimal for a false positive rate of 1% (0.01). +/// Formula used: k = -ln(p)/ln(2) ≈ 7 +/// where: +/// - k is the number of hash functions +/// - p is the desired false positive rate (0.01) +const HASH_COUNT: u8 = 7; + +/// Number of bits per element in the Bloom filter. +/// The value 9.6 is derived from the formula for optimal size: +/// m/n = -ln(p)/ln(2)² ≈ 9.6 +/// where: +/// - m is the size of the filter in bits +/// - n is the number of elements +/// - p is the desired false positive rate (0.01) +const BITS_PER_ELEMENT: f64 = 9.6; + +type BloomHasher = AHasher; + +pub struct EventBloomWriter { + filter: BloomFilter, +} + +impl fmt::Debug for EventBloomWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "EventBloomWriter {{ size: {}, hash_count: {} }}", self.size(), HASH_COUNT) + } +} + +impl Serialize for EventBloomWriter { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.filter.storage().serialize(serializer) + } +} + +impl EventBloomWriter { + pub fn from_events<'a, I>(events: I) -> Self + where + I: Iterator + 'a, + { + let mut unique_keys = HashSet::new(); + + for key in Self::events_to_bloom_keys(events) { + unique_keys.insert(key); + } + + let size = (unique_keys.len() as f64 * BITS_PER_ELEMENT).ceil() as _; + + let filter = BloomFilter::<_, AtomicBitStore>::new(size, HASH_COUNT); + + for key in unique_keys { + filter.add(&key); + } + Self { filter } + } + + pub fn size(&self) -> u64 { + self.filter.len() + } + + fn events_to_bloom_keys<'a, I>(events: I) -> impl Iterator + 'a + where + I: Iterator + 'a, + { + events.flat_map(Self::event_to_bloom_keys) + } + + fn event_to_bloom_keys(event: &Event) -> impl Iterator + '_ { + let from_address_key = create_bloom_key(0, &event.from_address); + + let keys = event.keys.iter().enumerate().map(|(i, key)| create_bloom_key(i as u8 + 1, key)); + + std::iter::once(from_address_key).chain(keys) + } +} + +pub struct EventBloomReader { + filter: BloomFilter, +} + +impl<'de> Deserialize<'de> for EventBloomReader { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let storage = BitStore::deserialize(deserializer)?; + + Ok(Self { filter: BloomFilter::from_storage(storage, HASH_COUNT) }) + } +} + +pub struct EventBloomSearcher { + patterns: Vec>, +} + +impl EventBloomSearcher { + pub fn new(from_address: Option<&Felt>, keys: Option<&[Vec]>) -> Self { + let mut patterns = Vec::new(); + + if let Some(from_address) = from_address { + let from_address_key = create_bloom_key(0, from_address); + patterns.push(vec![PreCalculatedHashes::new::(HASH_COUNT, &from_address_key)]); + } + + if let Some(keys) = keys { + for (i, key) in keys.iter().enumerate() { + if key.is_empty() { + continue; + } + patterns.push( + key.iter() + .map(|key| create_bloom_key(i as u8 + 1, key)) + .map(|key| PreCalculatedHashes::new::(HASH_COUNT, &key)) + .collect(), + ); + } + } + + Self { patterns } + } + + pub fn search(&self, filter: &EventBloomReader) -> bool { + self.patterns.iter().all(|pattern| pattern.iter().any(|hashes| filter.filter.might_contain_hashes(hashes))) + } +} + +fn create_bloom_key(index: u8, key: &Felt) -> [u8; 33] { + let mut bloom_key = [0u8; 33]; + bloom_key[0] = index; + bloom_key[1..].copy_from_slice(&key.to_bytes_be()); + bloom_key +} diff --git a/crates/madara/primitives/bloom_filter/src/filter.rs b/crates/madara/primitives/bloom_filter/src/filter.rs new file mode 100644 index 000000000..1eba2d9f8 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/filter.rs @@ -0,0 +1,272 @@ +//! Bloom filter implementation with configurable hash functions and storage backends. +//! +//! A Bloom filter is a space-efficient probabilistic data structure used to test whether an element +//! is a member of a set. False positive matches are possible, but false negatives are not. In other +//! words, a query returns either "possibly in set" or "definitely not in set." +//! +//! # Features +//! +//! - Generic over hash functions +//! - Supports both thread-safe (atomic) and single-threaded storage backends +//! - Pre-calculated hash optimization for repeated queries +//! - Serialization support via serde + +use crate::storage::{AtomicBitStore, BitStore}; +use serde::{Deserialize, Serialize}; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +/// A cache of computed hash positions that can be reused across different filter sizes. +/// +/// This structure stores the raw hash values generated for an item, allowing them to be +/// efficiently mapped to different filter sizes without recalculating the hash functions. +/// This is particularly useful when the same item needs to be checked against multiple +/// Bloom filters of different sizes. +/// +/// # Requirements +/// +/// The number of hash functions used to create this pre-calculated hash set must match +/// the number of hash functions used by any Bloom filter it's used with. Attempting to +/// use these hashes with a filter having a different hash count will result in incorrect +/// behavior. +#[derive(Debug, Clone)] +pub struct PreCalculatedHashes { + /// Raw hash values before modulo operation + raw_hashes: Vec, + /// Number of hash functions used + hash_count: u8, +} + +impl PreCalculatedHashes { + /// Creates a new instance with pre-calculated hash values for the given item. + /// + /// # Type Parameters + /// + /// * `H`: The hasher implementation to use (must implement `Hasher + Default`) + /// * `T`: The type of item being hashed (must implement `Hash`) + /// + /// # Parameters + /// + /// * `hash_count`: Number of hash functions to use (must match the target filter's hash count) + /// * `item`: Reference to the item to generate hashes for + /// + /// # Returns + /// + /// A new `PreCalculatedHashes` instance containing the computed hash values + pub fn new(hash_count: u8, item: &T) -> Self { + Self { raw_hashes: calculate_hashes::(hash_count, item).collect(), hash_count } + } + + /// Returns the number of hash functions used in these pre-calculations. + /// + /// This value represents how many different bit positions will be generated + /// for each filter size and must match the target filter's hash count. + #[inline] + pub fn hash_count(&self) -> u8 { + self.hash_count + } + + /// Returns a reference to the raw hash values before modulo operation. + /// + /// These values are the unmodified results from the hash functions, + /// before being mapped to specific filter sizes. + #[inline] + pub fn raw_hashes(&self) -> &[u64] { + &self.raw_hashes + } + + /// Calculates bit positions for a specific filter size using the pre-calculated hashes. + /// + /// This method maps the raw hash values to actual bit positions within a filter + /// of the specified size using modulo operation. + /// + /// # Parameters + /// + /// * `filter_size`: The size of the target Bloom filter in bits + /// + /// # Returns + /// + /// An iterator yielding `hash_count` bit positions, each in the range [0, filter_size) + /// + /// # Notes + /// + /// - The returned positions are guaranteed to be within the valid range for the + /// specified filter size, but may not be unique if collisions occur. + /// - The target Bloom filter must have the same number of hash functions as was used + /// to create these pre-calculated hashes. Using these positions with a filter that + /// has a different hash count will lead to incorrect behavior. + #[inline] + pub fn get_positions_for_size(&self, filter_size: u64) -> impl Iterator + '_ { + self.raw_hashes.iter().map(move |hash| (hash % filter_size) as usize) + } +} + +/// A Bloom filter implementation with configurable hash function and storage backend. +/// +/// # Type Parameters +/// +/// * `H`: The hasher implementation to use for generating hash values +/// * `B`: The storage backend type (either `AtomicBitStore` for mutable or `BitStore` for immutable) +/// +/// # Fields +/// +/// * `storage`: The bit array storage backend +/// * `bit_size`: The total number of bits in the filter +/// * `hash_count`: The number of hash functions used +/// * `_hasher`: Phantom data for the hasher type +#[derive(Serialize, Deserialize, Clone)] +pub struct BloomFilter { + storage: B, + bit_size: u64, + hash_count: u8, + #[serde(skip)] + _hasher: PhantomData, +} + +impl BloomFilter { + /// Creates a new Bloom filter with the specified size and number of hash functions. + /// + /// The actual size will be aligned to the next multiple of 64 bits for efficient storage. + /// + /// # Arguments + /// + /// * `size`: The desired size of the filter in bits + /// * `hash_count`: The number of hash functions to use + pub fn new(size: u64, hash_count: u8) -> Self { + let size_in_u64s = (size.max(1) + 63) >> 6; + let aligned_size = size_in_u64s << 6; + + Self { + storage: AtomicBitStore::new(size_in_u64s as _), + bit_size: aligned_size, + hash_count, + _hasher: PhantomData, + } + } + + /// Converts this mutable Bloom filter into an immutable one. + /// + /// This is useful when you're done adding elements and want to switch to a read-only version. + pub fn finalize(self) -> BloomFilter { + BloomFilter { + storage: self.storage.to_readonly(), + bit_size: self.bit_size, + hash_count: self.hash_count, + _hasher: PhantomData, + } + } + + /// Adds an item to the Bloom filter. + /// + /// # Arguments + /// + /// * `item`: The item to add, which must implement the Hash trait + pub fn add(&self, item: &T) { + calculate_hashes::(self.hash_count, item) + .for_each(|hash| self.storage.set_bit((hash % self.bit_size) as usize)); + } +} + +impl BloomFilter { + /// Returns the size of the Bloom filter in bits. + pub fn len(&self) -> u64 { + self.bit_size + } + + /// Returns the number of hash functions used by the filter. + pub fn hash_count(&self) -> u8 { + self.hash_count + } + + pub(crate) fn storage(&self) -> &B { + &self.storage + } +} + +impl BloomFilter { + /// Creates a new immutable Bloom filter from a bit storage backend. + pub fn from_storage(storage: BitStore, hash_count: u8) -> Self { + let bit_size = storage.len() as _; + Self { storage, bit_size, hash_count, _hasher: PhantomData } + } + + /// Tests whether an item might be in the set (immutable version). + pub fn might_contain(&self, item: &T) -> bool { + calculate_hashes::(self.hash_count, item) + .all(|hash| self.storage.test_bit((hash % self.bit_size) as usize)) + } + + /// Tests whether an item might be in the set using pre-calculated hash values. + pub fn might_contain_hashes(&self, pre_calculated: &PreCalculatedHashes) -> bool { + pre_calculated.get_positions_for_size(self.bit_size).all(|pos| self.storage.test_bit(pos)) + } + + /// Estimates the current false positive rate of the filter based on + /// the number of bits set and the number of hash functions. + pub fn estimated_false_positive_rate(&self) -> f64 { + let set_bits = self.storage.count_ones() as f64; + let total_bits = self.storage.len() as f64; + (set_bits / total_bits).powi(self.hash_count as i32) + } +} + +/// Calculates multiple hash positions for an item using double hashing technique. +/// +/// This function implements a double hashing strategy to generate k different bit +/// positions using the formula: +/// +/// h(i) = (h1 + i * h2) mod m +/// +/// where: +/// - h1 is the primary hash value of the item +/// - h2 is a secondary hash derived from h1 using rotation and golden ratio multiplication +/// - m is the bit array size (applied implicitly by the caller) +/// - i iterates from 0 to k-1, with k being the number of desired hash functions +/// +/// # Mathematical Properties +/// +/// ## Golden Ratio Constant (11400714819323198485) +/// The function uses PHI = 2^64/φ where φ is the golden ratio. This value is chosen because: +/// - It provides optimal distribution properties for hashing +/// - When used in multiplication, it ensures good bit avalanche effects +/// - Its binary representation has excellent bit distribution +/// - It minimizes correlation between input and output bits +/// +/// ## Bit Rotation (17) +/// The 17-bit rotation was selected because: +/// - It is coprime with 64 (word size), ensuring maximum period length +/// - Provides effective bit mixing while remaining computationally efficient +/// - As an odd number, it avoids potential patterns from even rotations +/// - Creates balanced mixing between upper and lower word bits +/// +/// The combination of golden ratio multiplication and 17-bit rotation ensures both +/// strong avalanche properties and computational efficiency. +/// +/// # Parameters +/// +/// * `hash_count`: Number of hash positions to generate +/// * `item`: Reference to the item to be hashed +/// +/// # Type Parameters +/// +/// * `H`: A hasher type that implements `Hasher` + `Default` +/// * `T`: The type of item being hashed, must implement `Hash` +/// +/// # Returns +/// +/// Returns an iterator yielding `hash_count` different hash values. Each value +/// represents a position that can be mapped to the underlying bit array through +/// modulo operation by the caller. +/// +/// Note: The actual bit positions should be computed by the caller by applying +/// modulo with the bit array size to the returned hash values. +#[inline] +fn calculate_hashes(hash_count: u8, item: &T) -> impl Iterator + '_ { + let mut hasher1 = H::default(); + item.hash(&mut hasher1); + let h1 = hasher1.finish(); + + const PHI: u64 = 0x9e37_79b9_7f4a_7c15; // 2^64/φ + let h2 = h1.rotate_left(17).wrapping_mul(PHI); + + (0..hash_count).map(move |i| h1.wrapping_add(h2.wrapping_mul(i as u64))) +} diff --git a/crates/madara/primitives/bloom_filter/src/lib.rs b/crates/madara/primitives/bloom_filter/src/lib.rs new file mode 100644 index 000000000..001d4a885 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/lib.rs @@ -0,0 +1,17 @@ +// mod config; +mod constants; +mod error; +mod events; +mod filter; +mod storage; + +// pub use config::BloomConfig; +pub use constants::*; +pub use error::BloomError; +pub use filter::{BloomFilter, PreCalculatedHashes}; +pub use storage::{AtomicBitStore, BitStore}; + +pub use events::{EventBloomReader, EventBloomSearcher, EventBloomWriter}; + +#[cfg(test)] +mod tests; diff --git a/crates/madara/primitives/bloom_filter/src/storage.rs b/crates/madara/primitives/bloom_filter/src/storage.rs new file mode 100644 index 000000000..5d118b547 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/storage.rs @@ -0,0 +1,204 @@ +//! Storage backends for Bloom filters. +//! +//! This module provides both atomic (thread-safe) and non-atomic storage implementations +//! for the bit array used by the Bloom filter. + +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt, + sync::atomic::{AtomicU64, Ordering}, +}; + +/// An immutable bit storage implementation using regular u64 values. +#[derive(Serialize, Deserialize, Clone)] +#[repr(transparent)] +pub struct BitStore { + bits: Box<[u64]>, +} + +/// A thread-safe bit storage implementation using atomic u64 values. +#[repr(transparent)] +pub struct AtomicBitStore { + bits: Box<[AtomicU64]>, +} + +#[cfg(test)] +impl Clone for AtomicBitStore { + fn clone(&self) -> Self { + let cloned_bits = self + .bits + .iter() + .map(|atomic| { + let value = atomic.load(std::sync::atomic::Ordering::Relaxed); + std::sync::atomic::AtomicU64::new(value) + }) + .collect::>() + .into_boxed_slice(); + + Self { bits: cloned_bits } + } +} + +/// Custom serialization for AtomicBitStore that converts atomic values to regular u64s. +impl Serialize for AtomicBitStore { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let regular_bits: Vec = self.bits.iter().map(|atomic| atomic.load(Ordering::Relaxed)).collect(); + regular_bits.serialize(serializer) + } +} + +/// Custom visitor for deserializing AtomicBitStore from regular u64 values. +struct AtomicBitStoreVisitor; + +impl<'de> Visitor<'de> for AtomicBitStoreVisitor { + type Value = AtomicBitStore; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an array of unsigned 64-bit integers") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut regular_bits = Vec::new(); + while let Some(value) = seq.next_element()? { + regular_bits.push(value); + } + + let atomic_bits = regular_bits.into_iter().map(AtomicU64::new).collect(); + + Ok(AtomicBitStore { bits: atomic_bits }) + } +} + +impl<'de> Deserialize<'de> for AtomicBitStore { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(AtomicBitStoreVisitor) + } +} + +impl BitStore { + /// Creates a new BitStore from a boxed slice of u64 values. + #[inline] + pub(crate) fn new_from_slice(bits: Box<[u64]>) -> Self { + Self { bits } + } + + /// Returns the total number of bits in the store. + #[inline] + pub(crate) fn len(&self) -> usize { + self.bits.len() << 6 + } + + /// Counts the total number of set bits (ones) in the store. + #[inline] + pub(crate) fn count_ones(&self) -> usize { + self.bits.iter().map(|&block| block.count_ones() as usize).sum() + } + + /// Tests whether a specific bit is set. + /// + /// # Arguments + /// + /// * `index`: The bit position to test + /// + /// # Implementation Notes + /// + /// The function uses direct indexing since input validation is performed at the hash + /// generation level, making additional bounds checking redundant. + #[inline] + pub(crate) fn test_bit(&self, index: usize) -> bool { + let (block_index, bit_mask) = bit_position(index); + self.bits[block_index] & bit_mask != 0 + } + + #[cfg(test)] + pub(crate) fn fingerprint(&self) -> u64 { + use std::hash::{Hash, Hasher}; + + let mut hasher = std::collections::hash_map::DefaultHasher::default(); + for b in &self.bits { + b.hash(&mut hasher); + } + hasher.finish() + } +} + +impl AtomicBitStore { + /// Creates a new AtomicBitStore with the specified size in u64 blocks. + pub(crate) fn new(size_in_u64s: usize) -> Self { + let bits = (0..size_in_u64s).map(|_| AtomicU64::new(0)).collect(); + Self { bits } + } + + /// Sets a specific bit to 1. + /// + /// # Arguments + /// + /// * `index`: The bit position to set + /// + /// # Implementation Notes + /// + /// The function uses direct indexing since input validation is performed at the hash + /// generation level, making additional bounds checking redundant. + #[inline] + pub(crate) fn set_bit(&self, index: usize) { + let (block_index, bit_mask) = bit_position(index); + self.bits[block_index].fetch_or(bit_mask, Ordering::Relaxed); + } + + /// Converts this atomic store into a regular BitStore. + pub(crate) fn to_readonly(self) -> BitStore { + let bits = self.bits.iter().map(|b| b.load(Ordering::Relaxed)).collect(); + BitStore::new_from_slice(bits) + } + + #[cfg(test)] + pub(crate) fn fingerprint(&self) -> u64 { + use std::hash::{Hash, Hasher}; + + let mut hasher = std::collections::hash_map::DefaultHasher::default(); + for b in &self.bits { + b.load(Ordering::Relaxed).hash(&mut hasher); + } + hasher.finish() + } +} + +/// Calculates the block index and bit mask for a given bit position. +/// +/// The bit position is split into two components: +/// - A block index identifying which u64 block contains the bit +/// - A bit mask with a single 1 at the target position within that block +/// +/// # Arguments +/// +/// * `index`: Global bit position in the bitset +/// +/// # Returns +/// +/// A tuple containing: +/// * `block_index`: Index of the u64 block (index >> 6 is equivalent to index / 64) +/// * `bit_mask`: A u64 with a single bit set (1 << position_in_block) +/// +/// # Implementation Notes +/// +/// - Uses bit shifts for efficient division/modulo: +/// - `>> 6` divides by 64 to get block index +/// - `& 0x3F` is equivalent to modulo 64 for bit position +/// - Operations are const and will be computed at compile time when possible +#[inline(always)] +const fn bit_position(index: usize) -> (usize, u64) { + // Calculate which u64 block contains our bit + let block_index = index >> 6; + // Create a mask with a 1 bit in the correct position (0-63) within the block + let bit_mask = 1u64 << (index & 0x3F); + (block_index, bit_mask) +} diff --git a/crates/madara/primitives/bloom_filter/src/tests/filter_tests.rs b/crates/madara/primitives/bloom_filter/src/tests/filter_tests.rs new file mode 100644 index 000000000..2a69ff338 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/tests/filter_tests.rs @@ -0,0 +1,191 @@ +use crate::*; +use rand::{thread_rng, Rng}; +use rayon::prelude::*; +use std::collections::{hash_map::DefaultHasher, HashSet}; +use tests::utils::{create_filter, FALSE_POSITIF_RATE, HASH_COUNT}; + +#[test] +fn test_basic_operations() { + const NB_ELEM: u64 = 2; + + let filter = create_filter::(NB_ELEM); + let key1 = b"Hello"; + let key2 = b"World"; + + // Test adding elements + filter.add(key1); + filter.add(key2); + + // Test conversion to read-only and lookups + let ro_filter = filter.finalize(); + assert!(ro_filter.might_contain(key1), "Filter should contain key1"); + assert!(ro_filter.might_contain(key2), "Filter should contain key2"); + assert!(!ro_filter.might_contain(b"other"), "Filter should not contain 'other'"); +} + +#[test] +fn test_false_positive_rate() { + const NB_ELEM: u64 = 100_000; + + let filter = create_filter::(NB_ELEM); + + // Add elements + for i in 0..NB_ELEM { + filter.add(&i); + } + + let read_only_filter = filter.finalize(); + + // Test false positive rate + let fp_rate = read_only_filter.estimated_false_positive_rate(); + assert!( + fp_rate <= FALSE_POSITIF_RATE * 1.1, + "False positive rate {} exceeds expected rate {} by more than 10%", + fp_rate, + FALSE_POSITIF_RATE + ); +} + +#[test] +fn test_parallel() { + const NB_ELEM: u64 = 10_000; + + let filter = create_filter::(NB_ELEM); + let elements: Vec = (0..NB_ELEM).collect(); + + // Test parallel insertion + elements.par_iter().for_each(|item| { + filter.add(item); + }); + + let ro_filter = filter.finalize(); + + // Test parallel lookup for false negatives + let false_negatives: Vec<_> = elements.par_iter().filter(|&&item| !ro_filter.might_contain(&item)).collect(); + + assert!(false_negatives.is_empty(), "Found {} false negatives which should be impossible", false_negatives.len()); +} + +#[test] +fn test_pre_calculated_hashes() { + const NB_ELEM: u64 = 1_000; + + let filter = create_filter::(NB_ELEM); + let test_item = "test_value"; + + // Create pre-calculated hashes + let pre_calc = PreCalculatedHashes::new::(HASH_COUNT, &test_item); + + // Test that hash count matches + assert_eq!(pre_calc.hash_count(), HASH_COUNT, "Pre-calculated hash count should match filter hash count"); + + filter.add(&test_item); + let ro_filter = filter.finalize(); + + // Test lookup with pre-calculated hashes + assert!(ro_filter.might_contain_hashes(&pre_calc), "Filter should find item using pre-calculated hashes"); +} + +#[test] +fn test_actual_false_positive_rate() { + const NB_ELEM: u64 = 10_000; + const TEST_SAMPLES: u64 = 100_000; + + let filter = create_filter::(NB_ELEM); + let mut rng = thread_rng(); + + // Create a set of known elements + let mut known_elements = HashSet::new(); + for _ in 0..NB_ELEM { + let value: u64 = rng.gen(); + known_elements.insert(value); + filter.add(&value); + } + + let ro_filter = filter.finalize(); + + // Test random elements and count false positives + let mut false_positives = 0; + for _ in 0..TEST_SAMPLES { + let test_value: u64 = rng.gen(); + if !known_elements.contains(&test_value) && ro_filter.might_contain(&test_value) { + false_positives += 1; + } + } + + let actual_fp_rate = false_positives as f64 / TEST_SAMPLES as f64; + println!("Actual false positive rate: {}", actual_fp_rate); + assert!( + actual_fp_rate <= FALSE_POSITIF_RATE * 1.1, + "Actual false positive rate {} significantly exceeds expected rate {}", + actual_fp_rate, + FALSE_POSITIF_RATE + ); +} + +#[test] +fn test_size_alignment() { + // Test that the filter size is properly aligned to 64-bit boundaries + let filter = BloomFilter::::new(100, HASH_COUNT); + assert_eq!(filter.len() % 64, 0, "Filter size should be aligned to 64 bits"); +} + +#[test] +fn test_empty_filter() { + let filter = create_filter::(0); + let ro_filter = filter.finalize(); + + // Test various types with empty filter + assert!(!ro_filter.might_contain(&42u64)); + assert!(!ro_filter.might_contain(&"test")); + assert!(!ro_filter.might_contain(&vec![1, 2, 3])); + assert!(!ro_filter.might_contain(&(21, "tuple"))); +} + +#[test] +fn test_different_types() { + let filter = create_filter::(10); + + // Test adding different types + filter.add(&42u64); + filter.add(&"string"); + filter.add(&vec![1, 2, 3]); + filter.add(&(21, "tuple")); + + let ro_filter = filter.finalize(); + + assert!(ro_filter.might_contain(&42u64)); + assert!(ro_filter.might_contain(&"string")); + assert!(ro_filter.might_contain(&vec![1, 2, 3])); + assert!(ro_filter.might_contain(&(21, "tuple"))); +} + +#[test] +fn test_concurrent_reads() { + const NB_ELEM: u64 = 1_000; + const NB_THREADS: usize = 8; + + let filter = create_filter::(NB_ELEM); + let test_value = "concurrent_test"; + filter.add(&test_value); + + let ro_filter = filter.finalize(); + let filter_arc = std::sync::Arc::new(ro_filter); + + // Spawn multiple threads to read concurrently + let handles: Vec<_> = (0..NB_THREADS) + .map(|_| { + let filter_clone = std::sync::Arc::clone(&filter_arc); + std::thread::spawn(move || { + for _ in 0..1000 { + assert!(filter_clone.might_contain(&test_value)); + } + }) + }) + .collect(); + + // Wait for all threads to complete + for handle in handles { + handle.join().unwrap(); + } +} diff --git a/crates/madara/primitives/bloom_filter/src/tests/mod.rs b/crates/madara/primitives/bloom_filter/src/tests/mod.rs new file mode 100644 index 000000000..7b0758f06 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/tests/mod.rs @@ -0,0 +1,6 @@ +#[cfg(test)] +mod filter_tests; +#[cfg(test)] +mod serialization; +#[cfg(test)] +mod utils; diff --git a/crates/madara/primitives/bloom_filter/src/tests/serialization.rs b/crates/madara/primitives/bloom_filter/src/tests/serialization.rs new file mode 100644 index 000000000..1d2ed2017 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/tests/serialization.rs @@ -0,0 +1,88 @@ +use crate::*; +use bincode::{deserialize, serialize}; +use std::collections::hash_map::DefaultHasher; +use tests::utils::create_filter; + +#[test] +fn test_basic_atomic_serialization() { + const NB_ELEM: u64 = 3; + + let filter = create_filter::(NB_ELEM); + + filter.add(&"test1"); + filter.add(&"test2"); + filter.add(&"test3"); + + let fingerprint = filter.storage().fingerprint(); + let serialized = serialize(&filter).unwrap(); + let deserialized: BloomFilter = deserialize(&serialized).unwrap(); + assert_eq!(fingerprint, deserialized.storage().fingerprint(), "Fingerprint mismatch"); + + let readonly = deserialized.finalize(); + + assert!(readonly.might_contain(&"test1")); + assert!(readonly.might_contain(&"test2")); + assert!(readonly.might_contain(&"test3")); + assert!(!readonly.might_contain(&"test4")); +} + +#[test] +fn test_basic_readonly_serialization() { + const NB_ELEM: u64 = 1; + + let filter = create_filter::(NB_ELEM); + + filter.add(&"test1"); + let readonly = filter.finalize(); + let fingerprint = readonly.storage().fingerprint(); + + // Sérialiser + let serialized = serialize(&readonly).unwrap(); + + // Désérialiser + let deserialized: BloomFilter = deserialize(&serialized).unwrap(); + + assert_eq!(fingerprint, deserialized.storage().fingerprint(), "Fingerprint mismatch"); + + assert!(deserialized.might_contain(&"test1")); + assert!(!deserialized.might_contain(&"test2")); +} + +#[test] +fn test_cross_type_serialization() { + const NB_ELEM: u64 = 3; + + let filter = create_filter::(NB_ELEM); + + filter.add(&"test1"); + filter.add(&"test2"); + + let serialized = serialize(&filter).unwrap(); + + let deserialized: BloomFilter = deserialize(&serialized).unwrap(); + + assert!(deserialized.might_contain(&"test1")); + assert!(deserialized.might_contain(&"test2")); + assert!(!deserialized.might_contain(&"test3")); +} + +#[test] +fn test_large_dataset_serialization() { + const NB_ELEM: u64 = 10_000; + + let filter = create_filter::(NB_ELEM); + + for i in 0..NB_ELEM { + filter.add(&format!("test{}", i)); + } + + let serialized = serialize(&filter).unwrap(); + + let _atomic: BloomFilter = deserialize(&serialized).unwrap(); + let readonly: BloomFilter = deserialize(&serialized).unwrap(); + + for i in 0..NB_ELEM { + let key = format!("test{}", i); + assert!(readonly.might_contain(&key)); + } +} diff --git a/crates/madara/primitives/bloom_filter/src/tests/utils.rs b/crates/madara/primitives/bloom_filter/src/tests/utils.rs new file mode 100644 index 000000000..8e3581d61 --- /dev/null +++ b/crates/madara/primitives/bloom_filter/src/tests/utils.rs @@ -0,0 +1,20 @@ +use std::hash::Hasher; + +use crate::{AtomicBitStore, BloomFilter}; + +// Constants for Bloom filter configuration +// Number of hash functions used in the Bloom filter. +// The value 7 is optimal for a false positive rate of 1% (0.01). +pub const HASH_COUNT: u8 = 7; + +// Number of bits per element in the Bloom filter. +// The value 9.6 is optimal for a false positive rate of 1% (0.01). +const BITS_PER_ELEMENT: f64 = 9.6; + +pub const FALSE_POSITIF_RATE: f64 = 0.01; + +/// Helper function to create a Bloom filter with the given number of elements +pub fn create_filter(nb_elem: u64) -> BloomFilter { + let size = (nb_elem as f64 * BITS_PER_ELEMENT).ceil() as u64; + BloomFilter::new(size, HASH_COUNT) +}