diff --git a/coprocessor/fhevm-engine/stress-test-generator/data/handles_for_pub_decryption b/coprocessor/fhevm-engine/stress-test-generator/data/handles_for_pub_decryption deleted file mode 100644 index 631c8268e..000000000 --- a/coprocessor/fhevm-engine/stress-test-generator/data/handles_for_pub_decryption +++ /dev/null @@ -1,7 +0,0 @@ -0xc165039a3fdd987dc1947974a1d5df33e5949af023ff0000000000aa36a70000 -0xd5e63c6300f3193eb1bda221e1b9bfd48bc8fe61a1ff0000000000aa36a70100 -0x1cd4bc32477d3564db1c91b1b1635097f866d33bfcff0000000000aa36a70200 -0xad15b3c866e45185abef655555e460c41cb8e298e9ff0000000000aa36a70300 -0x2d976a404cd2c7583cf5c5c8b8cde6c09f69236abfff0000000000aa36a70400 -0x4f4a919c8be17df0f6f172374c955e99122a15dc8aff0000000000aa36a70500 -0xe5a0bfc072f5813adbe2621799ff473e48417735b5ff0000000000aa36a70600 diff --git a/coprocessor/fhevm-engine/stress-test-generator/data/handles_for_usr_decryption b/coprocessor/fhevm-engine/stress-test-generator/data/handles_for_usr_decryption deleted file mode 100644 index 725090860..000000000 --- a/coprocessor/fhevm-engine/stress-test-generator/data/handles_for_usr_decryption +++ /dev/null @@ -1,7 +0,0 @@ -0x169ea9cd4ce1b58487de61d8c959473e8f135894fcff0000000000aa36a70000 -0xa8a03dd51f5959945068effb6033b970373737a655ff0000000000aa36a70100 -0xe18e3cc1c840501ce326eb93203ec1f771ffd8641bff0000000000aa36a70200 -0x122c8a1cd75b61906ea18d9d2b2ea8aeaf9f980f7aff0000000000aa36a70300 -0xe4b149fccb12661f1f74d71511b09483d23dff25cfff0000000000aa36a70400 -0x902bc88be7a4376e0c8977dce7799cf8f7ee7bf071ff0000000000aa36a70500 -0xabcbe6a2b97c672869769b5886f288c7e3fd2a538dff0000000000aa36a70600 diff --git a/coprocessor/fhevm-engine/stress-test-generator/data/minitest_003_generate_handles_for_decryption.csv b/coprocessor/fhevm-engine/stress-test-generator/data/minitest_003_generate_handles_for_decryption.csv index 8df1488cf..6f5fb0249 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/data/minitest_003_generate_handles_for_decryption.csv +++ b/coprocessor/fhevm-engine/stress-test-generator/data/minitest_003_generate_handles_for_decryption.csv @@ -1,2 +1,2 @@ -GenPubDecHandles; NA; Count; NA; NA; 0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08; 0x31De9c8ac5ECD5EacEddDdEE531e9BaD8AC9c2A5; 1.0; 1 -GenUsrDecHandles; NA; Count; NA; NA; 0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08; 0x31De9c8ac5ECD5EacEddDdEE531e9BaD8AC9c2A5; 1.0; 1 +GenPubDecHandles; NA; Count; NA; NA; 0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08; 0x885B871b70335f1A8111F4B2BEB533821Ef4b86C; 1.0; 1 +GenUsrDecHandles; NA; Count; NA; NA; 0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08; 0x885B871b70335f1A8111F4B2BEB533821Ef4b86C; 1.0; 1 diff --git a/test-suite/gateway-stress/.sqlx/query-2f49a66126dda8f3e5f043ad8fa119691568ca3216e1a04715aa02322bf3723d.json b/test-suite/gateway-stress/.sqlx/query-2f49a66126dda8f3e5f043ad8fa119691568ca3216e1a04715aa02322bf3723d.json new file mode 100644 index 000000000..a2cc1f374 --- /dev/null +++ b/test-suite/gateway-stress/.sqlx/query-2f49a66126dda8f3e5f043ad8fa119691568ca3216e1a04715aa02322bf3723d.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM user_decryption_responses", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "2f49a66126dda8f3e5f043ad8fa119691568ca3216e1a04715aa02322bf3723d" +} diff --git a/test-suite/gateway-stress/.sqlx/query-6007239279928f6691a5284666e99fb6f020f20264c157500dbad47d7ec3dfa9.json b/test-suite/gateway-stress/.sqlx/query-6007239279928f6691a5284666e99fb6f020f20264c157500dbad47d7ec3dfa9.json new file mode 100644 index 000000000..f04adf649 --- /dev/null +++ b/test-suite/gateway-stress/.sqlx/query-6007239279928f6691a5284666e99fb6f020f20264c157500dbad47d7ec3dfa9.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM public_decryption_responses", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "6007239279928f6691a5284666e99fb6f020f20264c157500dbad47d7ec3dfa9" +} diff --git a/test-suite/gateway-stress/.sqlx/query-6471556ae0071cc8896a01ad0f2f350416bf00d6d617422422f2368f5ec7c826.json b/test-suite/gateway-stress/.sqlx/query-6471556ae0071cc8896a01ad0f2f350416bf00d6d617422422f2368f5ec7c826.json new file mode 100644 index 000000000..aaebbeec9 --- /dev/null +++ b/test-suite/gateway-stress/.sqlx/query-6471556ae0071cc8896a01ad0f2f350416bf00d6d617422422f2368f5ec7c826.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM public_decryption_requests", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "6471556ae0071cc8896a01ad0f2f350416bf00d6d617422422f2368f5ec7c826" +} diff --git a/test-suite/gateway-stress/.sqlx/query-7eb5ee37fa8e57c641712b895a5f59e0c484429e78626ccd5ad8b6d55a12267b.json b/test-suite/gateway-stress/.sqlx/query-7eb5ee37fa8e57c641712b895a5f59e0c484429e78626ccd5ad8b6d55a12267b.json new file mode 100644 index 000000000..b4fe34379 --- /dev/null +++ b/test-suite/gateway-stress/.sqlx/query-7eb5ee37fa8e57c641712b895a5f59e0c484429e78626ccd5ad8b6d55a12267b.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM user_decryption_requests", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "7eb5ee37fa8e57c641712b895a5f59e0c484429e78626ccd5ad8b6d55a12267b" +} diff --git a/test-suite/gateway-stress/.sqlx/query-a9ac11a0896006a03fd4260810c31bf236ebe89054f4c7e981490c596799585b.json b/test-suite/gateway-stress/.sqlx/query-a9ac11a0896006a03fd4260810c31bf236ebe89054f4c7e981490c596799585b.json new file mode 100644 index 000000000..acd7b6dd8 --- /dev/null +++ b/test-suite/gateway-stress/.sqlx/query-a9ac11a0896006a03fd4260810c31bf236ebe89054f4c7e981490c596799585b.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT 1 AS health", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "health", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "a9ac11a0896006a03fd4260810c31bf236ebe89054f4c7e981490c596799585b" +} diff --git a/test-suite/gateway-stress/.sqlx/query-affa510bdee616839e36215c598d07a20ca7af56c37fca94c0c1759dc2eba8ea.json b/test-suite/gateway-stress/.sqlx/query-affa510bdee616839e36215c598d07a20ca7af56c37fca94c0c1759dc2eba8ea.json new file mode 100644 index 000000000..cd101e552 --- /dev/null +++ b/test-suite/gateway-stress/.sqlx/query-affa510bdee616839e36215c598d07a20ca7af56c37fca94c0c1759dc2eba8ea.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT decryption_id, created_at FROM user_decryption_responses WHERE decryption_id = ANY($1::bytea[])", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "decryption_id", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "created_at", + "type_info": "Timestamp" + } + ], + "parameters": { + "Left": [ + "ByteaArray" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "affa510bdee616839e36215c598d07a20ca7af56c37fca94c0c1759dc2eba8ea" +} diff --git a/test-suite/gateway-stress/.sqlx/query-d6597f8cda1d06ba8a5adedc650047a8646bfa1f3c666f2cf0f29257268b3542.json b/test-suite/gateway-stress/.sqlx/query-d6597f8cda1d06ba8a5adedc650047a8646bfa1f3c666f2cf0f29257268b3542.json new file mode 100644 index 000000000..b5f8692a9 --- /dev/null +++ b/test-suite/gateway-stress/.sqlx/query-d6597f8cda1d06ba8a5adedc650047a8646bfa1f3c666f2cf0f29257268b3542.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT decryption_id, created_at FROM public_decryption_responses WHERE decryption_id = ANY($1::bytea[])", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "decryption_id", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "created_at", + "type_info": "Timestamp" + } + ], + "parameters": { + "Left": [ + "ByteaArray" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "d6597f8cda1d06ba8a5adedc650047a8646bfa1f3c666f2cf0f29257268b3542" +} diff --git a/test-suite/gateway-stress/Cargo.lock b/test-suite/gateway-stress/Cargo.lock index a08db2407..955fd9728 100644 --- a/test-suite/gateway-stress/Cargo.lock +++ b/test-suite/gateway-stress/Cargo.lock @@ -153,15 +153,16 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad451f9a70c341d951bca4e811d74dbe1e193897acd17e9dbac1353698cc430b" +checksum = "59094911f05dbff1cf5b29046a00ef26452eccc8d47136d50a47c0cf22f00c85" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-serde", - "alloy-trie", + "alloy-trie 0.9.1", + "alloy-tx-macros", "auto_impl", "c-kzg", "derive_more", @@ -171,15 +172,16 @@ dependencies = [ "rand 0.8.5", "secp256k1", "serde", + "serde_json", "serde_with", "thiserror 2.0.12", ] [[package]] name = "alloy-consensus-any" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142daffb15d5be1a2b20d2cd540edbcef03037b55d4ff69dc06beb4d06286dba" +checksum = "903cb8f728107ca27c816546f15be38c688df3c381d7bd1a4a9f215effc1ddb4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -226,9 +228,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.1.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cc14d832bc3331ca22a1c7819de1ede99f58f61a7d123952af7dde8de124a6" +checksum = "a3f56873f3cac7a2c63d8e98a4314b8311aa96adb1a0f82ae923eb2119809d2c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -278,9 +280,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3056872f6da48046913e76edb5ddced272861f6032f09461aea1a2497be5ae5d" +checksum = "ac7f1c9a1ccc7f3e03c36976455751a6166a4f0d2d2c530c3f87dfe7d0cdc836" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -293,7 +295,9 @@ dependencies = [ "derive_more", "either", "serde", + "serde_with", "sha2", + "thiserror 2.0.12", ] [[package]] @@ -305,15 +309,15 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-serde", - "alloy-trie", + "alloy-trie 0.8.1", "serde", ] [[package]] name = "alloy-json-abi" -version = "1.1.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ccaa79753d7bf15f06399ea76922afbfaf8d18bebed9e8fc452984b4a90dcc9" +checksum = "125a1c373261b252e53e04d6e92c37d881833afc1315fceab53fd46045695640" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -323,12 +327,13 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc08b31ebf9273839bd9a01f9333cbb7a3abb4e820c312ade349dd18bdc79581" +checksum = "65f763621707fa09cece30b73ecc607eb43fd7a72451fe3b46f645b905086926" dependencies = [ "alloy-primitives", "alloy-sol-types", + "http 1.3.1", "serde", "serde_json", "thiserror 2.0.12", @@ -337,9 +342,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.9" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed117b08f0cc190312bf0c38c34cf4f0dabfb4ea8f330071c587cd7160a88cb2" +checksum = "f18959e1a1b40e05578e7a705f65ff4e6b354e38335da4b33ccbee876bde7c26" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -363,9 +368,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7162ff7be8649c0c391f4e248d1273e85c62076703a1f3ec7daf76b283d886d" +checksum = "46e9374c667c95c41177602ebe6f6a2edd455193844f011d973d374b65501b38" dependencies = [ "alloy-consensus", "alloy-eips", @@ -376,9 +381,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.1.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c35fc4b03ace65001676358ffbbaefe2a2b27ee50fe777c345082c7c888be8" +checksum = "bc9485c56de23438127a731a6b4c87803d49faf1a7068dcd1d8768aca3a9edb9" dependencies = [ "alloy-rlp", "bytes", @@ -526,9 +531,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.19" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1e99233cff99aff94fe29cea9e9dd6014c85eeea7890ea4f0e4eada9957ceb" +checksum = "07429a1099cd17227abcddb91b5e38c960aaeb02a6967467f5bb561fbe716ac6" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -537,9 +542,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf7dff0fdd756a714d58014f4f8354a1706ebf9fa2cf73431e0aeec3c9431e" +checksum = "db46b0901ee16bbb68d986003c66dcb74a12f9d9b3c44f8e85d51974f2458f0f" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -552,14 +557,15 @@ dependencies = [ "itertools 0.14.0", "serde", "serde_json", + "serde_with", "thiserror 2.0.12", ] [[package]] name = "alloy-serde" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730e8f2edf2fc224cabd1c25d090e1655fa6137b2e409f92e5eec735903f1507" +checksum = "5413814be7a22fbc81e0f04a2401fcc3eb25e56fd53b04683e8acecc6e1fe01b" dependencies = [ "alloy-primitives", "serde", @@ -568,9 +574,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.5" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59245704a5dbd20b93913f4a20aa41b45c4c134f82e119bb54de4b627e003955" +checksum = "42084a7b455ef0b94ed201b7494392a759c3e20faac2d00ded5d5762fcf71dee" dependencies = [ "alloy-primitives", "async-trait", @@ -601,9 +607,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.5" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae78644ab0945e95efa2dc0cfac8f53aa1226fe85c294a0d8ad82c5cc9f09a2" +checksum = "6312ccc048a4a88aed7311fc448a2e23da55c60c2b3b6dcdb794f759d02e49d7" dependencies = [ "alloy-consensus", "alloy-network", @@ -615,13 +621,14 @@ dependencies = [ "k256", "rand 0.8.5", "thiserror 2.0.12", + "zeroize", ] [[package]] name = "alloy-sol-macro" -version = "1.1.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8612e0658964d616344f199ab251a49d48113992d81b92dab93ed855faa66383" +checksum = "d20d867dcf42019d4779519a1ceb55eba8d7f3d0e4f0a89bcba82b8f9eb01e48" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -633,9 +640,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.1.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a384edac7283bc4c010a355fb648082860c04b826bb7a814c45263c8f304c74" +checksum = "b74e91b0b553c115d14bd0ed41898309356dc85d0e3d4b9014c4e7715e48c8ad" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -652,9 +659,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "1.1.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd588c2d516da7deb421b8c166dc60b7ae31bca5beea29ab6621fcfa53d6ca5" +checksum = "84194d31220803f5f62d0a00f583fd3a062b36382e2bea446f1af96727754565" dependencies = [ "alloy-json-abi", "const-hex", @@ -680,9 +687,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.1.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584cb97bfc5746cb9dcc4def77da11694b5d6d7339be91b7480a6a68dc129387" +checksum = "f5383d34ea00079e6dd89c652bcbdb764db160cef84e6250926961a0b2295d04" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -756,12 +763,41 @@ dependencies = [ "alloy-rlp", "arrayvec", "derive_more", - "nybbles", + "nybbles 0.3.4", "serde", "smallvec", "tracing", ] +[[package]] +name = "alloy-trie" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec", + "derive_more", + "nybbles 0.4.5", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "alloy-tx-macros" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64c09ec565a90ed8390d82aa08cd3b22e492321b96cb4a3d4f58414683c9e2f" +dependencies = [ + "alloy-primitives", + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1096,6 +1132,12 @@ dependencies = [ "rayon", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -1196,6 +1238,15 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1729,7 +1780,7 @@ checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bc2wrap" version = "2.0.1" -source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.11.0-26#5618c3d8131a994980c560fa5c508e258025605c" +source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.12.0#dcb36704f6852af6f042da3541228dff0fd297fd" dependencies = [ "bincode 2.0.1", "serde", @@ -1862,6 +1913,9 @@ name = "bitflags" version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" +dependencies = [ + "serde", +] [[package]] name = "bitvec" @@ -1885,6 +1939,19 @@ dependencies = [ "nom 7.1.3", ] +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -2014,9 +2081,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -2061,9 +2128,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", "clap_derive", @@ -2071,9 +2138,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -2083,9 +2150,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -2165,6 +2232,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "config" version = "0.15.14" @@ -2248,6 +2324,12 @@ dependencies = [ "typewit", ] +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "core-foundation" version = "0.9.4" @@ -2339,6 +2421,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -2477,8 +2568,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -2495,13 +2596,39 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "serde", + "strsim", + "syn 2.0.106", +] + [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", "quote", "syn 2.0.106", ] @@ -2621,6 +2748,12 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "downcast" version = "0.11.0" @@ -2810,6 +2943,28 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -2861,7 +3016,7 @@ dependencies = [ [[package]] name = "fhevm_gateway_bindings" version = "0.1.0-rc14" -source = "git+https://github.com/zama-ai/fhevm.git?tag=v0.8.0#080c7878a89a37dc75d1d0b2eacd78c9073cadbe" +source = "git+https://git@github.com/zama-ai/fhevm.git?tag=v0.9.0-1#bf397ab8138e1f1d6df7e2a79182c8d93b59a722" dependencies = [ "alloy", "serde", @@ -2870,7 +3025,7 @@ dependencies = [ [[package]] name = "fhevm_gateway_bindings" version = "0.1.0-rc14" -source = "git+https://git@github.com/zama-ai/fhevm.git?rev=2c9fb79fa557feb4c113f1482f0a4805e9d6acac#2c9fb79fa557feb4c113f1482f0a4805e9d6acac" +source = "git+https://github.com/zama-ai/fhevm.git?tag=v0.9.1-1#ece670b96e8314e9a640f736b58e991343401b11" dependencies = [ "alloy", "serde", @@ -2900,6 +3055,17 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2981,6 +3147,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -3064,14 +3241,14 @@ checksum = "312d2295c7302019c395cfb90dacd00a82a2eabd700429bba9c7a3f38dbbe11b" [[package]] name = "gateway-sdk" -version = "0.1.0" -source = "git+https://github.com/zama-ai/fhevm.git?tag=v0.8.0#080c7878a89a37dc75d1d0b2eacd78c9073cadbe" +version = "0.9.0" +source = "git+https://github.com/zama-ai/fhevm.git?tag=v0.9.1-1#ece670b96e8314e9a640f736b58e991343401b11" dependencies = [ "alloy", "bc2wrap", "bincode 1.3.3", "crypto_box", - "fhevm_gateway_bindings 0.1.0-rc14 (git+https://git@github.com/zama-ai/fhevm.git?rev=2c9fb79fa557feb4c113f1482f0a4805e9d6acac)", + "fhevm_gateway_bindings 0.1.0-rc14 (git+https://git@github.com/zama-ai/fhevm.git?tag=v0.9.0-1)", "futures-util", "hex", "kms", @@ -3091,7 +3268,7 @@ dependencies = [ [[package]] name = "gateway-stress" -version = "0.1.0" +version = "0.9.0" dependencies = [ "alloy", "anyhow", @@ -3102,16 +3279,19 @@ dependencies = [ "clap", "config", "csv", - "fhevm_gateway_bindings 0.1.0-rc14 (git+https://github.com/zama-ai/fhevm.git?tag=v0.8.0)", + "fhevm_gateway_bindings 0.1.0-rc14 (git+https://github.com/zama-ai/fhevm.git?tag=v0.9.1-1)", "futures", "gateway-sdk", "humantime", "humantime-serde", "indicatif", + "rand 0.9.2", "rustls 0.23.31", "serde", "serde_json", + "sqlx", "tokio", + "tokio-util", "tracing", "tracing-subscriber", ] @@ -3277,6 +3457,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -3313,6 +3502,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -3872,8 +4070,8 @@ dependencies = [ [[package]] name = "kms" -version = "0.11.0-26" -source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.11.0-26#5618c3d8131a994980c560fa5c508e258025605c" +version = "0.12.0" +source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.12.0#dcb36704f6852af6f042da3541228dff0fd297fd" dependencies = [ "aes", "aes-gcm", @@ -3889,7 +4087,6 @@ dependencies = [ "bc2wrap", "bincode 2.0.1", "bip39", - "bytes", "cbc", "cfg-if", "clap", @@ -3920,7 +4117,6 @@ dependencies = [ "thiserror 2.0.12", "threshold-fhe", "tokio-util", - "tonic-build", "tracing", "trait-variant", "url", @@ -3931,9 +4127,10 @@ dependencies = [ [[package]] name = "kms-grpc" -version = "0.11.0-26" -source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.11.0-26#5618c3d8131a994980c560fa5c508e258025605c" +version = "0.12.0" +source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.12.0#dcb36704f6852af6f042da3541228dff0fd297fd" dependencies = [ + "alloy-dyn-abi", "alloy-primitives", "alloy-sol-types", "anyhow", @@ -4010,6 +4207,27 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4389,6 +4607,20 @@ dependencies = [ "smallvec", ] +[[package]] +name = "nybbles" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa11e84403164a9f12982ab728f3c67c6fd4ab5b5f0254ffc217bdbd3b28ab0" +dependencies = [ + "alloy-rlp", + "cfg-if", + "proptest", + "ruint", + "serde", + "smallvec", +] + [[package]] name = "object" version = "0.36.7" @@ -4491,6 +4723,12 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.4" @@ -4645,6 +4883,12 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "poly1305" version = "0.8.0" @@ -5109,9 +5353,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -5744,10 +5988,11 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -5762,11 +6007,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -5832,7 +6086,7 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn 2.0.106", @@ -6021,6 +6275,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -6042,6 +6299,204 @@ dependencies = [ "der 0.7.10", ] +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64 0.22.1", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.5", + "hashlink", + "indexmap 2.10.0", + "log", + "memchr", + "once_cell", + "percent-encoding", + "rustls 0.23.31", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.12", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.106", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.106", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "digest 0.10.7", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.12", + "time", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.12", + "time", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.12", + "time", + "tracing", + "url", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -6066,6 +6521,17 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" @@ -6181,12 +6647,13 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "tfhe" -version = "1.3.2" +version = "1.4.0-alpha.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bb534e32676812f15855fe6eee497c7f3e8346057783895cd0eadf9d1a1f6b" +checksum = "217d37960449b4c3c9a6c9cf60982bd1f7eaaa9f6a5e64aa4abfc2bfe91fad60" dependencies = [ "aligned-vec", "bincode 1.3.3", + "blake3", "bytemuck", "dyn-stack", "itertools 0.14.0", @@ -6206,13 +6673,16 @@ dependencies = [ [[package]] name = "tfhe-csprng" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06450766ae375cd305281c5d691a41d8877e40d3cc7510a33244775d62f60ad4" +checksum = "58dbda4dff1373da441b7d1f131873c0229a09294327288acdbade48b38d128d" dependencies = [ "aes", + "getrandom 0.2.15", "libc", "rayon", + "serde", + "tfhe-versionable", ] [[package]] @@ -6232,9 +6702,9 @@ dependencies = [ [[package]] name = "tfhe-ntt" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c64925e25d6ba666f75332ce44fbd2e80be64dbd657e3c2652a6a07f2868d17" +checksum = "27e21dbb266f4472337dec6a608ffca2cf911d680b52873db8027c572f22d0fc" dependencies = [ "aligned-vec", "bytemuck", @@ -6243,9 +6713,9 @@ dependencies = [ [[package]] name = "tfhe-versionable" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d853f69b5e95332fa76424525cc403f34645838927212609776e26f8beab52c5" +checksum = "f59040e53a3581e1270a18eec2f1798e7bfd1c724b0f5d9db3f2a6f6cff89195" dependencies = [ "aligned-vec", "num-complex", @@ -6255,9 +6725,9 @@ dependencies = [ [[package]] name = "tfhe-versionable-derive" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a8431efbbcda5c501d6573928ebbc9c99f4d0ffd34b228395cf948e33f443a" +checksum = "b9a5d55a09b8aff152dee2fa3d28af8999c3af3f69a8bce728855de21638727e" dependencies = [ "proc-macro2", "quote", @@ -6266,9 +6736,9 @@ dependencies = [ [[package]] name = "tfhe-zk-pok" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ed6d29772b0f7e6021b7d2c3fe789cad26e3e8e49ef6f8a18887f16a6a5bfa7" +checksum = "8f7b4fa4d6b7d0bd2b5d37e237e2628dc464fc09d40ad4b95fcd3c5d2461cf98" dependencies = [ "ark-bls12-381", "ark-ec", @@ -6343,8 +6813,8 @@ dependencies = [ [[package]] name = "threshold-fhe" -version = "0.11.0-26" -source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.11.0-26#5618c3d8131a994980c560fa5c508e258025605c" +version = "0.12.0" +source = "git+https://git@github.com/zama-ai/kms.git?tag=v0.12.0#dcb36704f6852af6f042da3541228dff0fd297fd" dependencies = [ "aes", "aes-prng", @@ -6358,6 +6828,7 @@ dependencies = [ "dashmap", "derive_more", "futures", + "futures-util", "g2p", "getrandom 0.2.15", "hex", @@ -6819,6 +7290,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -6834,6 +7311,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-width" version = "0.2.1" @@ -6949,7 +7432,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca" dependencies = [ - "darling", + "darling 0.20.11", "once_cell", "proc-macro-error2", "proc-macro2", @@ -6963,6 +7446,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -7014,6 +7503,12 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -7151,6 +7646,16 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + [[package]] name = "wide" version = "0.7.33" @@ -7285,6 +7790,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -7312,6 +7826,21 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -7354,6 +7883,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -7366,6 +7901,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -7378,6 +7919,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -7402,6 +7949,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -7414,6 +7967,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -7426,6 +7985,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -7438,6 +8003,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/test-suite/gateway-stress/Cargo.toml b/test-suite/gateway-stress/Cargo.toml index 04981e72d..2bd3ece6f 100644 --- a/test-suite/gateway-stress/Cargo.toml +++ b/test-suite/gateway-stress/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "gateway-stress" -version = "0.1.0" +version = "0.9.0" edition = "2024" [dependencies] -fhevm_gateway_bindings = { git = "https://github.com/zama-ai/fhevm.git", tag = "v0.8.0", default-features = false } -gateway-sdk = { git = "https://github.com/zama-ai/fhevm.git", tag = "v0.8.0", default-features = false } +fhevm_gateway_bindings = { git = "https://github.com/zama-ai/fhevm.git", tag = "v0.9.1-1", default-features = false } +gateway-sdk = { git = "https://github.com/zama-ai/fhevm.git", tag = "v0.9.1-1", default-features = false } alloy = { version = "1.0", default-features = false, features = [ "essentials", @@ -30,19 +30,30 @@ indicatif = { version = "0.18.0", default-features = true, features = [ "tokio", ] } rustls = "0.23.31" -serde = { version = "1.0.219", default-features = false, features = [ +serde = { version = "1.0.226", default-features = false, features = [ "derive", "std", ] } serde_json = { version = "1.0.141", default-features = false, features = [ "std", ] } +sqlx = { version = "0.8.6", default-features = false, features = [ + "chrono", + "derive", + "macros", + "postgres", + "runtime-tokio", + "time", + "tls-rustls", +] } tokio = { version = "1.47.0", default-features = false, features = [ "macros", "rt-multi-thread", "sync", ] } +tokio-util = "0.7.15" tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", default-features = true, features = [ "env-filter", ] } +rand = "0.9.2" diff --git a/test-suite/gateway-stress/README.md b/test-suite/gateway-stress/README.md index 392c4ad64..ac7aa2d40 100644 --- a/test-suite/gateway-stress/README.md +++ b/test-suite/gateway-stress/README.md @@ -25,13 +25,15 @@ build of the Docker images for the tool. ## Configuration -To configure the tool, you can use either a configuration file (TOML format) or environment -variables, or both. +To configure the tool, you can must use a configuration file (TOML format). -Every configuration option is documented in the example [configuration file](config/config.toml), -along with its associated environment variable. +Every configuration option is documented in the example [configuration file](config/config.toml). -Configuration fields defined via environment variables override those in the configuration file. +Some of the configuration fields can be overridden via the CLI: +- `tests_duration` +- `tests_interval` +- `parallel_requests` +- `sequential` ## Run @@ -43,25 +45,32 @@ Once the `gateway-stress` binary has been built, you can run the following comma # Display CLI help ./gateway-stress help -# Run public decryption stress test with the given configuration file -./gateway-stress -c config/config.toml public +# Run public decryption stress test using the Gateway chain +./gateway-stress -c config/config.toml gw -t public -# Run user decryption stress test with the given configuration file -./gateway-stress -c config/config.toml user +# Run user decryption stress test using the Gateway chain +./gateway-stress -c config/config.toml gw -t user -# Run public decryption stress test with the given configuration file and env variable -PARALLEL_REQUESTS=10 ./gateway-stress -c config/config.toml public +# Override number of parallel requests during the stress tests session +./gateway-stress -c config/config.toml -p 10 gw public + +# Run public decryption stress test by inserting requests in Connectors' DBs +./gateway-stress -c config/config.toml db -t public + +# Run user decryption stress test by inserting requests in Connectors' DBs +./gateway-stress -c config/config.toml db -t user + +# Don't clear Connectors' DBs before and after running stress tests (not recommended) +./gateway-stress -c config/config.toml db -t public --skip_clear-db ``` Or directly from `test-suite/gateway-stress` directory: ```bash -cargo run -- -c config/config.toml public -cargo run -- -c config/config.toml user +cargo run -- -c config/config.toml gw -t public +cargo run -- -c config/config.toml gw -t user ``` -Note that the `mixed` command of the CLI is not implemented yet. - ### Benchmarking The `benchmark` command take a CSV file in input (and the global config file as well). @@ -77,11 +86,18 @@ and throughput) for each burst in a CSV file. ```bash # Run a benchmarking session using `templates/small_bench.csv` as input and store the global -# results in `/tmp/bench.csv` -./gateway-stress -c config/config.toml benchmark -i templates/small_bench.csv -o /tmp/bench.csv +# results in `/tmp/bench.csv` (decryption requests sent using the Gateway chain) +./gateway-stress -c config/config.toml bench-gw -i templates/small_bench.csv -o /tmp/bench.csv + +# Same, but also store each burst result in `tmp/full.csv` +./gateway-stress -c config/config.toml bench-gw -i templates/small_bench.csv -o /tmp/bench.csv -r /tmp/full.csv + +# Run a benchmarking session using `templates/small_bench.csv` as input and store the global +# results in `/tmp/bench.csv` (decryption requests inserted in Connectors' DB) +./gateway-stress -c config/config.toml bench-db -i templates/small_bench.csv -o /tmp/bench.csv # Same, but also store each burst result in `tmp/full.csv` -./gateway-stress -c config/config.toml benchmark -i templates/small_bench.csv -o /tmp/bench.csv -r /tmp/full.csv +./gateway-stress -c config/config.toml bench-db -i templates/small_bench.csv -o /tmp/bench.csv -r /tmp/full.csv ``` ## Tracing @@ -94,13 +110,13 @@ session, you can get more logs by configuring the `RUST_LOG` environment variabl ```bash # Enabling "DEBUG" traces of the stress test tool alone -RUST_LOG="gateway_stress=debug" ./gateway-stress -c config/config.toml public +RUST_LOG="gateway_stress=debug" ./gateway-stress -c config/config.toml gw -t public # Enabling "DEBUG" traces of the stress test tool and of the alloy crate -RUST_LOG="gateway_stress=debug,alloy=debug" ./gateway-stress -c config/config.toml public +RUST_LOG="gateway_stress=debug,alloy=debug" ./gateway-stress -c config/config.toml gw -t public # Enabling "DEBUG" traces for all crates used by the stress test tool -RUST_LOG="debug" ./gateway-stress -c config/config.toml public +RUST_LOG="debug" ./gateway-stress -c config/config.toml gw -t public ``` ## Bonus: Generating handles via coprocessor stress-test-generator diff --git a/test-suite/gateway-stress/config/config.toml b/test-suite/gateway-stress/config/config.toml index 697c0c344..1ba69969d 100644 --- a/test-suite/gateway-stress/config/config.toml +++ b/test-suite/gateway-stress/config/config.toml @@ -1,73 +1,74 @@ +# The contract allowed for user decrytion on user_ct_handles +allowed_contract = "0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08" + +# The time during which the tool will send request bursts to the Gateway/Connectors' DBs +# Format documentation: https://docs.rs/humantime/latest/humantime/ +tests_duration = "500ms" + +# The time to wait between two request bursts +# Format documentation: https://docs.rs/humantime/latest/humantime/ +tests_interval = "1s" + +# The number of parallel requests to send to the Gateway/Connectors' DBs, at each interval +parallel_requests = 1 + +# # If requests burst must be sent sequentially only when the previous one has been completed +# # Defaults to false, if set to true, then `tests_interval` is ignored +# sequential = false + +# The ciphertexts to use for user decryptions +[[user_ct]] +# The handle used of the ciphertext +handle = "0xc07ece86473ba540de4dbef63889f0233f5481dd7dff00000000000030390500" +# The digest of the ciphertext +digest = "0x134b4f97e70be0e71a26e38304e096fd370188b5c11b89feb8a61176b73d1b5a" + +# The ciphertexts to use for public decryptions +[[public_ct]] +# The handle used of the ciphertext +handle = "0x57539e394a6ae1dc4b4f1b00248777ce265eb6d08bff00000000000030390500" +# The digest of the ciphertext +digest = "0x134b4f97e70be0e71a26e38304e096fd370188b5c11b89feb8a61176b73d1b5a" + +# Configuration section for stress test using the Gateway chain +[blockchain] # Gateway WebSocket RPC URL endpoint (required) -# ENV: GATEWAY_URL -gateway_url = "ws://localhost:8546" +gateway_url = "http://localhost:8546" # Chain ID for the Host network (required) -# ENV: HOST_CHAIN_ID host_chain_id = 12345 # Chain ID for the Gateway network (required) -# ENV: GATEWAY_CHAIN_ID gateway_chain_id = 54321 # Address of the Decryption contract (required) -# ENV: DECRYPTION_ADDRESS -decryption_address = "0xc9bAE822fE6793e3B456144AdB776D5A318CB71e" +decryption_address = "0xF0bFB159C7381F7CB332586004d8247252C5b816" -# The time during which the tool will send requests burst to the Gateway -# Format documentation: https://docs.rs/humantime/latest/humantime/ -# ENV: TESTS_DURATION -tests_duration = "500ms" - -# If requests burst must be sent sequentially when the previous one has been completed -# Defaults to false, if set to true, then `tests_interval` is ignored -# ENV: SEQUENTIAL -# sequential = false - -# The interval between two requests burst -# Format documentation: https://docs.rs/humantime/latest/humantime/ -# ENV: TESTS_INTERVAL -tests_interval = "1s" +# Wallet's private key as a hex string +private_key = "24af7cb5f6cd0f29df22c6f3e2f18ee5b3949f5a489a14b0674bef8fd89bfe91" -# The number of parallel requests to send to the Gateway, at each interval -# ENV: PARALLEL_REQUESTS -parallel_requests = 5 -# The handles used for user decryptions -# ENV: USER_CT_HANDLES (format: comma separated list) -# Example: USER_CT_HANDLES="0x408a1510fb9ebf45651a6b0d40ce34c52a4bfc6338ff00000000000030390500,0x408a1510fb9ebf45651a6b0d40ce34c52a4bfc6338ff00000000000030390500" -user_ct_handles = [ - "0x7e314aadb1fe25d0eedc986529cf05604b71b717fdff00000000000030390500", +# Configuration section for stress test using the Connectors' DBs +[database] +# The URLs of the Connectors' DBs +urls = [ + "postgresql://postgres:postgres@localhost:5432/kms-connector" ] -# The handles used for public decryptions -# ENV: PUBLIC_CT_HANDLES (format: comma separated list) -# Example: PUBLIC_CT_HANDLES="0xbbb647e5a8dba46ed6f2eff8aed91d96ce9a80f537ff00000000000030390500,0xbbb647e5a8dba46ed6f2eff8aed91d96ce9a80f537ff00000000000030390500" -public_ct_handles = [ - "0xcc0fe2694c954beeea00716b20dbff2f4aa3756afbff00000000000030390500", -] +# The pool size for each connection to a Connector's DB +pool_size = 10 -# The contract allowed for user decrytion on ct_handles -# ENV: ALLOWED_CONTRACTS -allowed_contract = "0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08" +# The timeout for the connection to a Connector's DB +connection_timeout = "30s" -# Wallet's private key as a hex string (optional if `aws_kms_config` or `mnemonic` is configured) -# ENV: PRIVATE_KEY -private_key = "24af7cb5f6cd0f29df22c6f3e2f18ee5b3949f5a489a14b0674bef8fd89bfe91" +# The number of requests to insert in one insertion query (should be smaller than the +# `events_batch_size` setting of the KMS Workers) +insertion_chunk_size = 10 + +# The key ID that will be used by the inserted UserDecryptionRequest +key_id = "1809251394333065553493296640760748560207343510400633813116524750123642650625" -# Wallet's mnemonic (optional if `aws_kms_config` or `private_key` is configured) -# ENV: MNEMONIC -# mnemonic = "coyote sketch defense hover finger envelope celery urge panther venue verb cheese" - -# Wallet's mnemonic index (optional, defaults to 0) -# ENV: MNEMONIC_INDEX -# mnemonic_index = 0 - -# Wallet's AWS KMS configuration (optional if `private_key` or `mnemonic` is configured) -# [aws_kms_config] -# AWS KMS key ID for signing -# key_id = "12345678-1234-1234-1234-123456789012" -# Optional: AWS region for KMS (if not using default from AWS credentials) -# region = "us-east-1" -# Optional: AWS endpoint URL for KMS (for testing or custom endpoints) -# endpoint = "https://kms.us-east-1.amazonaws.com" +# The wallet address of the coprocessor's transaction sender. This is set in the inserted +# decryption requests, to be used by the KMS Workers to fetch the S3 URL from the Gateway before +# retrieving SnsCiphertextMaterial from S3. +copro_tx_sender_addr = "0x6254a198f67ad40290a2e7b48adb2d19b71f67bd" diff --git a/test-suite/gateway-stress/scripts/gen_handles.py b/test-suite/gateway-stress/scripts/gen_handles.py new file mode 100755 index 000000000..43a9b9089 --- /dev/null +++ b/test-suite/gateway-stress/scripts/gen_handles.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +from dataclasses import dataclass +import os +import tomllib + +SCRIPT_DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +COPRO_TOOL_DIR_PATH = ( + f"{SCRIPT_DIR_PATH}/../../../coprocessor/fhevm-engine/stress-test-generator" +) + + +@dataclass +class CiphertextHandle: + handle: str + digest64: str + digest128: str + + @classmethod + def from_line(cls, line: str): + return cls(*line.strip().split(" ")) + + +def generate_handle_with_copro_tool(): + _ = os.system(f""" + cd {COPRO_TOOL_DIR_PATH} && + rm -f data/handles*; + EVGEN_SCENARIO=data/minitest_003_generate_handles_for_decryption.csv make run; + cd - + """) + + +def edit_handles_in_config(): + with open(f"{COPRO_TOOL_DIR_PATH}/data/handles_for_usr_decryption") as usr_ct_file: + usr_ct_handles = [ + CiphertextHandle.from_line(line) for line in usr_ct_file.readlines() + ] + euint64_pub_ct = usr_ct_handles[5] + with open(f"{COPRO_TOOL_DIR_PATH}/data/handles_for_pub_decryption") as pub_ct_file: + pub_ct_handles = [ + CiphertextHandle.from_line(line) for line in pub_ct_file.readlines() + ] + euint64_usr_ct = pub_ct_handles[5] + print("euint64_pub_ct:", euint64_pub_ct) + print("euint64_usr_ct:", euint64_usr_ct) + + with open(f"{SCRIPT_DIR_PATH}/../config/config.toml", "rb") as config_file: + parsed_config = tomllib.load(config_file) + + with open(f"{SCRIPT_DIR_PATH}/../config/config.toml") as config_file: + new_config = ( + config_file.read() + .replace(parsed_config["user_ct"][0]["handle"], euint64_usr_ct.handle) + .replace(parsed_config["user_ct"][0]["digest"], euint64_usr_ct.digest128) + .replace(parsed_config["public_ct"][0]["handle"], euint64_pub_ct.handle) + .replace(parsed_config["public_ct"][0]["digest"], euint64_pub_ct.digest128) + ) + + with open(f"{SCRIPT_DIR_PATH}/../config/config.toml", "w+") as config_file: + _ = config_file.write(new_config) + + +def main(): + generate_handle_with_copro_tool() + edit_handles_in_config() + + +if __name__ == "__main__": + main() diff --git a/test-suite/gateway-stress/src/bench.rs b/test-suite/gateway-stress/src/bench.rs index 4dd7fae6b..bcb12914b 100644 --- a/test-suite/gateway-stress/src/bench.rs +++ b/test-suite/gateway-stress/src/bench.rs @@ -1,6 +1,8 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -use crate::decryption::BurstResult; +use crate::decryption::{ + BurstResult, + types::{DecryptionType, decryption_type_from_str, decryption_type_serialize}, +}; +use serde::{Deserialize, Serialize, Serializer}; #[derive(Debug, Deserialize)] pub struct BenchRecordInput { @@ -76,36 +78,6 @@ impl BenchBurstResult { } } -#[derive(Copy, Clone, Debug)] -pub enum DecryptionType { - Public, - User, -} - -fn decryption_type_from_str<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?.to_lowercase(); - if s == "u" || s.starts_with("user") { - Ok(DecryptionType::User) - } else if s == "p" || s.starts_with("public") { - Ok(DecryptionType::Public) - } else { - Err(serde::de::Error::custom("Invalid decryption type")) - } -} - -fn decryption_type_serialize(d: &DecryptionType, s: S) -> Result -where - S: Serializer, -{ - match d { - DecryptionType::Public => s.serialize_str("public"), - DecryptionType::User => s.serialize_str("user"), - } -} - fn f64_precision_two_serialize(float: &f64, s: S) -> Result where S: Serializer, diff --git a/test-suite/gateway-stress/src/app.rs b/test-suite/gateway-stress/src/blockchain/manager.rs similarity index 71% rename from test-suite/gateway-stress/src/app.rs rename to test-suite/gateway-stress/src/blockchain/manager.rs index ee40602d9..9251bed03 100644 --- a/test-suite/gateway-stress/src/app.rs +++ b/test-suite/gateway-stress/src/blockchain/manager.rs @@ -1,14 +1,16 @@ use crate::{ - bench::{BenchAverageResult, BenchBurstResult, BenchRecordInput, DecryptionType}, + bench::{BenchAverageResult, BenchBurstResult, BenchRecordInput}, blockchain::{ provider::{FillersWithoutNonceManagement, NonceManagedProvider}, wallet::Wallet, }, - cli::BenchmarkArgs, + cli::{GwBenchmarkArgs, GwTestArgs}, config::Config, decryption::{ EVENT_LISTENER_POLLING, init_public_decryption_response_listener, - init_user_decryption_response_listener, public_decryption_burst, user_decryption_burst, + init_user_decryption_response_listener, public::PublicDecryptThresholdEvent, + public_decryption_burst, types::DecryptionType, user::UserDecryptThresholdEvent, + user_decryption_burst, }, }; use alloy::{ @@ -16,15 +18,11 @@ use alloy::{ primitives::Address, providers::{ Identity, ProviderBuilder, RootProvider, - fillers::{ChainIdFiller, FillProvider, JoinFill, WalletFiller}, + fillers::{ChainIdFiller, JoinFill, WalletFiller}, }, - rpc::types::Log, - sol_types, }; use anyhow::anyhow; -use fhevm_gateway_bindings::decryption::Decryption::{ - self, DecryptionInstance, PublicDecryptionResponse, UserDecryptionResponse, -}; +use fhevm_gateway_bindings::decryption::Decryption::{self, DecryptionInstance}; use futures::Stream; use gateway_sdk::{FhevmSdk, FhevmSdkBuilder}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; @@ -37,18 +35,16 @@ use tokio::{ use tracing::{Instrument, info}; /// The provider used to interact with the Gateway. -type AppProvider = NonceManagedProvider< - FillProvider< - JoinFill< - JoinFill, FillersWithoutNonceManagement>, - WalletFiller, - >, - RootProvider, +pub type AppProvider = NonceManagedProvider< + JoinFill< + JoinFill, FillersWithoutNonceManagement>, + WalletFiller, >, + RootProvider, >; /// A struct used to perform the load/stress testing of the Gateway. -pub struct App { +pub struct GatewayTestManager { /// The decryption contract instance. decryption_contract: DecryptionInstance, @@ -62,7 +58,7 @@ pub struct App { sdk: Arc, } -impl App { +impl GatewayTestManager { /// Connects the tool to the Gateway. pub async fn connect(config: Config) -> anyhow::Result { INSTALL_CRYPTO_PROVIDER_ONCE.call_once(|| { @@ -72,26 +68,30 @@ impl App { .unwrap() }); - let wallet = Wallet::from_config(&config).await?; + let Some(blockchain_config) = config.blockchain.as_ref() else { + return Err(anyhow!("Missing [blockchain] section in config file")); + }; + + let wallet = Wallet::from_config(blockchain_config).await?; let provider = NonceManagedProvider::new( ProviderBuilder::new() .disable_recommended_fillers() - .with_chain_id(config.gateway_chain_id) + .with_chain_id(blockchain_config.gateway_chain_id) .filler(FillersWithoutNonceManagement::default()) .wallet(wallet.clone()) - .connect_http(config.gateway_url.parse()?), + .connect_http(blockchain_config.gateway_url.parse()?), wallet.address(), ); info!("Successfully connected to the Gateway"); - let decryption_contract = Decryption::new(config.decryption_address, provider); + let decryption_contract = Decryption::new(blockchain_config.decryption_address, provider); let sdk = Arc::new( FhevmSdkBuilder::new() - .with_gateway_chain_id(config.gateway_chain_id) - .with_decryption_contract(&config.decryption_address.to_string()) + .with_gateway_chain_id(blockchain_config.gateway_chain_id) + .with_decryption_contract(&blockchain_config.decryption_address.to_string()) .with_acl_contract(&Address::ZERO.to_string()) .with_input_verification_contract(&Address::ZERO.to_string()) - .with_host_chain_id(config.host_chain_id) + .with_host_chain_id(blockchain_config.host_chain_id) .build()?, ); @@ -103,12 +103,14 @@ impl App { }) } - /// Performs the public decryption stress test. - pub async fn public_decryption_stress_test(&self) -> anyhow::Result<()> { + /// Runs a decryption stress testing session via the Gateway chain. + pub async fn stress_test(&self, args: GwTestArgs) -> anyhow::Result<()> { let progress_tracker = MultiProgress::new(); - let response_listener = + let pub_response_listener = init_public_decryption_response_listener(self.decryption_contract.clone()).await?; - tokio::time::sleep(EVENT_LISTENER_POLLING).await; // Sleep for listener to be ready + let user_response_listener = + init_user_decryption_response_listener(self.decryption_contract.clone()).await?; + tokio::time::sleep(EVENT_LISTENER_POLLING).await; // Sleep for listeners to be ready let session_start = Instant::now(); let mut interval = interval(self.config.tests_interval); @@ -126,68 +128,29 @@ impl App { let (requests_pb, responses_pb) = init_progress_bars(&self.config, &progress_tracker, burst_index)?; - burst_tasks.spawn( - public_decryption_burst( + match args.decryption_type { + DecryptionType::Public => burst_tasks.spawn( + public_decryption_burst( + burst_index, + self.config.clone(), + self.decryption_contract.clone(), + pub_response_listener.clone(), + requests_pb, + responses_pb, + ) + .in_current_span(), + ), + DecryptionType::User => burst_tasks.spawn(user_decryption_burst( burst_index, self.config.clone(), self.decryption_contract.clone(), - response_listener.clone(), + Arc::clone(&self.sdk), + self.wallet.address(), + user_response_listener.clone(), requests_pb, responses_pb, - ) - .in_current_span(), - ); - - burst_index += 1; - - if self.config.sequential { - burst_tasks.join_next().await; - } - } - - burst_tasks.join_all().await; - let elapsed = session_start.elapsed().as_secs_f64(); - info!( - "Handled all burst in {:.2}s. Throughput: {:.2} tps", - elapsed, - (self.config.parallel_requests * (burst_index - 1) as u32) as f64 / elapsed - ); - Ok(()) - } - - /// Performs the user decryption stress test. - pub async fn user_decryption_stress_test(&self) -> anyhow::Result<()> { - let progress_tracker = MultiProgress::new(); - let response_listener = - init_user_decryption_response_listener(self.decryption_contract.clone()).await?; - tokio::time::sleep(EVENT_LISTENER_POLLING).await; // Sleep for listener to be ready - - let session_start = Instant::now(); - let mut interval = interval(self.config.tests_interval); - let mut burst_tasks = JoinSet::new(); - let mut burst_index = 1; - loop { - if !self.config.sequential { - interval.tick().await; - } - - if session_start.elapsed() > self.config.tests_duration { - break; - } - - let (requests_pb, responses_pb) = - init_progress_bars(&self.config, &progress_tracker, burst_index)?; - - burst_tasks.spawn(user_decryption_burst( - burst_index, - self.config.clone(), - self.decryption_contract.clone(), - Arc::clone(&self.sdk), - self.wallet.address(), - response_listener.clone(), - requests_pb, - responses_pb, - )); + )), + }; burst_index += 1; @@ -206,8 +169,8 @@ impl App { Ok(()) } - /// Runs a decryption benchmark session. - pub async fn decryption_benchmark(&self, args: BenchmarkArgs) -> anyhow::Result<()> { + /// Runs a decryption benchmark session via the Gateway chain. + pub async fn decryption_benchmark(&self, args: GwBenchmarkArgs) -> anyhow::Result<()> { let progress_tracker = MultiProgress::new(); let pub_response_listener = init_public_decryption_response_listener(self.decryption_contract.clone()).await?; @@ -264,14 +227,8 @@ impl App { user_response_listener: Arc>, ) -> anyhow::Result> where - PS: Stream> - + Unpin - + Send - + 'static, - US: Stream> - + Unpin - + Send - + 'static, + PS: Stream + Unpin + Send + 'static, + US: Stream + Unpin + Send + 'static, { let mut config = self.config.clone(); config.parallel_requests = bench_record.parallel_requests; diff --git a/test-suite/gateway-stress/src/blockchain/mod.rs b/test-suite/gateway-stress/src/blockchain/mod.rs index 4c611a44e..d4ff5fc1f 100644 --- a/test-suite/gateway-stress/src/blockchain/mod.rs +++ b/test-suite/gateway-stress/src/blockchain/mod.rs @@ -1,2 +1,5 @@ +pub mod manager; pub mod provider; pub mod wallet; + +pub use manager::GatewayTestManager; diff --git a/test-suite/gateway-stress/src/blockchain/provider.rs b/test-suite/gateway-stress/src/blockchain/provider.rs index a8ffc4f91..a2970e7cc 100644 --- a/test-suite/gateway-stress/src/blockchain/provider.rs +++ b/test-suite/gateway-stress/src/blockchain/provider.rs @@ -1,7 +1,7 @@ use alloy::{ consensus::Account, - eips::{BlockId, BlockNumberOrTag}, - network::{Network, TransactionBuilder}, + eips::{BlockId, BlockNumberOrTag, Encodable2718}, + network::{Ethereum, Network, TransactionBuilder}, primitives::{ Address, B256, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, U64, U128, U256, @@ -11,50 +11,95 @@ use alloy::{ PendingTransaction, PendingTransactionBuilder, PendingTransactionConfig, PendingTransactionError, Provider, ProviderCall, RootProvider, RpcWithBlock, SendableTx, fillers::{ - BlobGasFiller, CachedNonceManager, ChainIdFiller, GasFiller, JoinFill, NonceManager, + BlobGasFiller, CachedNonceManager, FillProvider, GasFiller, JoinFill, NonceManager, + TxFiller, }, }, rpc::{ client::NoParams, types::{ AccessListResult, Bundle, EIP1186AccountProofResponse, EthCallResponse, FeeHistory, - Filter, FilterChanges, Index, Log, SyncStatus, + Filter, FilterChanges, Index, Log, SyncStatus, TransactionReceipt, TransactionRequest, erc4337::TransactionConditional, pubsub::{Params, SubscriptionKind}, simulate::{SimulatePayload, SimulatedBlock}, }, }, - transports::TransportResult, + transports::{TransportError, TransportResult}, }; use futures::lock::Mutex; use serde_json::value::RawValue; use std::{borrow::Cow, sync::Arc}; -pub type FillersWithoutNonceManagement = - JoinFill>; +pub type FillersWithoutNonceManagement = JoinFill; /// A wrapper around an `alloy` provider that recovers its nonce manager on error. /// /// Note that the provider given by the user must not have nonce management enabled, as this /// is done by the `NonceManagedProvider` itself. /// Users can use the default `FillersWithoutNonceManagement` to create a provider. -pub struct NonceManagedProvider

{ - inner: P, +pub struct NonceManagedProvider +where + N: Network, + F: TxFiller, + P: Provider, +{ + inner: FillProvider, signer_address: Address, nonce_manager: Arc>, } -impl

NonceManagedProvider

{ - pub fn new(provider: P, signer_address: Address) -> Self { +impl NonceManagedProvider +where + F: TxFiller, + P: Provider, +{ + pub fn new(provider: FillProvider, signer_address: Address) -> Self { Self { inner: provider, signer_address, nonce_manager: Default::default(), } } + + pub async fn send_transaction_sync( + &self, + mut tx: TransactionRequest, + ) -> TransportResult { + let nonce = self + .nonce_manager + .lock() + .await + .get_next_nonce(&self.inner, self.signer_address) + .await?; + tx.set_nonce(nonce); + + let mut tx_bytes = Vec::new(); + self.inner + .fill(tx) + .await? + .try_into_envelope() + .map_err(|e| TransportError::LocalUsageError(Box::new(e)))? + .encode_2718(&mut tx_bytes); + + let res = self + .client() + .request("eth_sendRawTransactionSync", (Bytes::from(tx_bytes),)) + .await; + if res.is_err() { + // Reset the nonce manager if the transaction sending failed. + *self.nonce_manager.lock().await = Default::default(); + } + res + } } -impl Clone for NonceManagedProvider

{ +impl Clone for NonceManagedProvider +where + N: Network, + F: TxFiller, + P: Provider + Clone, +{ fn clone(&self) -> Self { Self { inner: self.inner.clone(), @@ -65,8 +110,10 @@ impl Clone for NonceManagedProvider

{ } #[async_trait::async_trait] -impl Provider for NonceManagedProvider

+impl Provider for NonceManagedProvider where + N: Network, + F: TxFiller, P: Provider, { fn root(&self) -> &RootProvider { diff --git a/test-suite/gateway-stress/src/blockchain/wallet.rs b/test-suite/gateway-stress/src/blockchain/wallet.rs index 89070b892..8bdb68f47 100644 --- a/test-suite/gateway-stress/src/blockchain/wallet.rs +++ b/test-suite/gateway-stress/src/blockchain/wallet.rs @@ -1,18 +1,11 @@ -use crate::config::{AwsKmsConfig, Config}; +use crate::config::BlockchainConfig; use alloy::{ hex::decode, network::{EthereumWallet, IntoWallet}, primitives::{Address, ChainId}, - signers::{ - Signer, - aws::AwsSigner, - k256::ecdsa::SigningKey, - local::{MnemonicBuilder, PrivateKeySigner, coins_bip39::English}, - }, + signers::{Signer, aws::AwsSigner, k256::ecdsa::SigningKey, local::PrivateKeySigner}, }; use anyhow::anyhow; -use aws_config::BehaviorVersion; -use aws_sdk_kms::Client as KmsClient; use tracing::{debug, info}; #[derive(Clone, Debug)] @@ -20,6 +13,7 @@ pub struct Wallet { signer: WalletSigner, } +#[allow(dead_code)] #[derive(Clone, Debug)] enum WalletSigner { Local(PrivateKeySigner), @@ -27,44 +21,15 @@ enum WalletSigner { } impl Wallet { - pub async fn from_config(config: &Config) -> anyhow::Result { - if let Some(aws_config) = &config.aws_kms_config { - debug!("Building wallet using AWS KMS configuration..."); - Self::from_aws_kms(aws_config.clone(), Some(config.gateway_chain_id)).await - } else if let Some(mnemonic) = &config.mnemonic { - debug!("Building wallet using mnemonic..."); - Self::from_mnemonic_with_index( - mnemonic, - config.mnemonic_index, - Some(config.gateway_chain_id), - ) - } else if let Some(private_key) = &config.private_key { - debug!("Building wallet using private key..."); - Self::from_private_key_str(private_key, Some(config.gateway_chain_id)) - } else { - Err(anyhow!( - "Either aws_kms or private_key should be configured" - )) - } - .inspect(|w| info!("Wallet built successfully, with address: {}!", w.address())) - } - - fn from_mnemonic_with_index( - phrase: &str, - account_index: usize, - chain_id: Option, - ) -> anyhow::Result { - let derivation_path = format!("m/44'/60'/0'/0/{}", account_index); - - let signer = MnemonicBuilder::::default() - .phrase(phrase) - .derivation_path(&derivation_path)? - .build()? - .with_chain_id(chain_id); - - Ok(Self { - signer: WalletSigner::Local(signer), - }) + pub async fn from_config(config: &BlockchainConfig) -> anyhow::Result { + debug!("Building wallet using private key..."); + let wallet = + Self::from_private_key_str(&config.private_key, Some(config.gateway_chain_id))?; + info!( + "Wallet built successfully, with address: {}!", + wallet.address() + ); + Ok(wallet) } fn from_private_key_str(private_key: &str, chain_id: Option) -> anyhow::Result { @@ -87,40 +52,6 @@ impl Wallet { }) } - async fn from_aws_kms( - aws_kms_config: AwsKmsConfig, - chain_id: Option, - ) -> anyhow::Result { - info!( - "Creating wallet from AWS KMS with key ID: {}", - aws_kms_config.key_id - ); - - let mut config_loader = aws_config::defaults(BehaviorVersion::latest()); - - if let Some(region) = aws_kms_config.region { - debug!("Using AWS region: {}", region); - config_loader = config_loader.region(aws_config::Region::new(region)); - } - - if let Some(endpoint) = aws_kms_config.endpoint { - debug!("Using AWS endpoint: {}", endpoint); - config_loader = config_loader.endpoint_url(endpoint); - } - - let config = config_loader.load().await; - let kms_client = KmsClient::new(&config); - let aws_signer = AwsSigner::new(kms_client, aws_kms_config.key_id, chain_id).await?; - - info!( - "Created wallet from AWS KMS with address: {}", - aws_signer.address() - ); - Ok(Self { - signer: WalletSigner::AwsKms(aws_signer), - }) - } - pub fn address(&self) -> Address { match &self.signer { WalletSigner::Local(signer) => signer.address(), diff --git a/test-suite/gateway-stress/src/cli.rs b/test-suite/gateway-stress/src/cli.rs index 9f816f64d..7ff499bb3 100644 --- a/test-suite/gateway-stress/src/cli.rs +++ b/test-suite/gateway-stress/src/cli.rs @@ -1,14 +1,15 @@ +use crate::decryption::types::DecryptionType; use clap::{Args, Parser, Subcommand, command}; -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr, time::Duration}; #[derive(Parser)] #[command(version, about, long_about = None)] pub struct Cli { /// The path to the testing configuration file #[arg(short, long, value_name = "FILE")] - pub config: Option, + pub config: PathBuf, - /// Enable sequential sending of burst requests + /// Enable sequential sending of request burst #[arg(short, long, default_value_t = false)] pub sequential: bool, @@ -16,27 +17,76 @@ pub struct Cli { #[arg(short, long)] pub parallel: Option, + /// Sets the duration of the test session + #[arg(short, long)] + #[clap(value_parser = humantime::parse_duration)] + pub duration: Option, + + /// Sets the time to wait between each request burst + #[arg(short, long)] + #[clap(value_parser = humantime::parse_duration)] + pub interval: Option, + #[command(subcommand)] pub subcommand: Subcommands, } #[derive(Subcommand)] pub enum Subcommands { - /// Perform tests with public decryptions only - Public, + /// Perform decryption stress tests using the Gateway chain + Gw(GwTestArgs), + + /// Perform decryption benchmark using the Gateway chain + BenchGw(GwBenchmarkArgs), + + /// Perform stress tests by inserting decryption requests directly in connectors' DB + Db(DbTestArgs), + + /// Perform decryption benchmark by inserting decryption requests directly in connectors' DB + BenchDb(DbBenchmarkArgs), +} + +#[derive(Args, Debug)] +pub struct GwTestArgs { + /// Sets the type of decryption for the test session + #[arg(short = 't', long)] + #[clap(value_parser = DecryptionType::from_str, default_value = "public")] + pub decryption_type: DecryptionType, +} + +#[derive(Args)] +pub struct GwBenchmarkArgs { + /// CSV input file describing the benchmarks to run + #[arg(short, long)] + pub input: PathBuf, + + /// CSV output file containing the benchmarks results summary + #[arg(short, long)] + pub output: PathBuf, - /// Perform tests with user decryptions only - User, + /// Optional CSV output file containing the full benchmarks results + #[arg(short, long)] + pub results: Option, +} - /// Perform tests with mixed decryptions (both public and user) - Mixed, +#[derive(Args, Debug)] +pub struct DbTestArgs { + /// Skip the database tables' clear before and after running the tests + #[arg(long, default_value = "false")] + pub skip_clear_db: bool, - /// Perform decryption benchmark - Benchmark(BenchmarkArgs), + /// Sets the type of decryption for the test session + #[arg(short = 't', long)] + #[clap(value_parser = DecryptionType::from_str, default_value = "public")] + pub decryption_type: DecryptionType, } #[derive(Args)] -pub struct BenchmarkArgs { +pub struct DbBenchmarkArgs { + /// Skip the database tables' clear before and after running the tests + #[arg(long, default_value = "false")] + pub skip_clear_db: bool, + /// CSV input file describing the benchmarks to run #[arg(short, long)] pub input: PathBuf, diff --git a/test-suite/gateway-stress/src/config.rs b/test-suite/gateway-stress/src/config.rs index 759acceaa..070cd6c83 100644 --- a/test-suite/gateway-stress/src/config.rs +++ b/test-suite/gateway-stress/src/config.rs @@ -1,23 +1,12 @@ -use alloy::primitives::{Address, FixedBytes}; -use config::{Config as ConfigBuilder, Environment, File, FileFormat}; -use serde::{Deserialize, Deserializer, Serialize}; -use std::{path::Path, str::FromStr, time::Duration}; +use alloy::primitives::{Address, FixedBytes, U256}; +use config::{Config as ConfigBuilder, File, FileFormat}; +use serde::Deserialize; +use std::{path::Path, time::Duration}; -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize)] pub struct Config { - pub gateway_url: String, - pub host_chain_id: u64, - pub gateway_chain_id: u64, - pub decryption_address: Address, - pub private_key: Option, - pub mnemonic: Option, - #[serde(default = "default_mnemonic_index")] - pub mnemonic_index: usize, - pub aws_kms_config: Option, - #[serde(deserialize_with = "parse_ct_handles")] - pub user_ct_handles: Vec>, - #[serde(deserialize_with = "parse_ct_handles")] - pub public_ct_handles: Vec>, + pub user_ct: Vec, + pub public_ct: Vec, pub allowed_contract: Address, pub parallel_requests: u32, #[serde(with = "humantime_serde")] @@ -26,52 +15,53 @@ pub struct Config { pub tests_interval: Duration, #[serde(default)] pub sequential: bool, + #[serde(default)] + pub blockchain: Option, + #[serde(default)] + pub database: Option, } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct AwsKmsConfig { - pub key_id: String, - pub region: Option, - pub endpoint: Option, +#[derive(Clone, Debug, Deserialize)] +pub struct CiphertextConfig { + pub handle: FixedBytes<32>, + pub digest: FixedBytes<32>, } -impl Config { - pub fn from_env_and_file>(path: Option

) -> anyhow::Result { - let mut builder = ConfigBuilder::builder(); - - // If path is provided, add it as a config source - if let Some(path) = path { - builder = builder.add_source( - File::with_name(path.as_ref().to_str().unwrap()).format(FileFormat::Toml), - ); - } +#[derive(Clone, Debug, Deserialize)] +pub struct BlockchainConfig { + pub gateway_url: String, + pub host_chain_id: u64, + pub gateway_chain_id: u64, + pub decryption_address: Address, + pub private_key: String, +} - builder = builder.add_source( - Environment::default() - .list_separator(",") - .with_list_parse_key("public_ct_handles") - .with_list_parse_key("user_ct_handles") - .try_parsing(true), - ); +#[derive(Clone, Debug, Deserialize)] +pub struct DatabaseConfig { + pub urls: Vec, + #[serde(default = "default_pool_size")] + pub pool_size: u32, + #[serde(with = "humantime_serde", default = "default_db_connection_timeout")] + pub connection_timeout: Duration, + pub key_id: U256, + pub copro_tx_sender_addr: Address, + pub insertion_chunk_size: usize, +} +impl Config { + pub fn from_env_and_file>(path: P) -> anyhow::Result { + let builder = ConfigBuilder::builder() + .add_source(File::with_name(path.as_ref().to_str().unwrap()).format(FileFormat::Toml)); let settings = builder.build()?; let config = settings.try_deserialize()?; Ok(config) } } -fn default_mnemonic_index() -> usize { - 0 +fn default_pool_size() -> u32 { + 10 } -fn parse_ct_handles<'de, D>(d: D) -> Result>, D::Error> -where - D: Deserializer<'de>, -{ - let ct_handles = Vec::::deserialize(d)? - .iter() - .map(|h| FixedBytes::from_str(h.as_str()).expect("Invalid handle: {h}")) - .collect(); - - Ok(ct_handles) +fn default_db_connection_timeout() -> Duration { + Duration::from_secs(30) } diff --git a/test-suite/gateway-stress/src/db/connector.rs b/test-suite/gateway-stress/src/db/connector.rs new file mode 100644 index 000000000..bf80faaa8 --- /dev/null +++ b/test-suite/gateway-stress/src/db/connector.rs @@ -0,0 +1,177 @@ +use crate::{ + config::DatabaseConfig, + db::types::{DecryptionRequestDbMetadata, SnsCiphertextMaterialDbItem}, + decryption::types::DecryptionRequest, +}; +use fhevm_gateway_bindings::decryption::Decryption::{ + PublicDecryptionRequest, UserDecryptionRequest, +}; +use sqlx::{Executor, Pool, Postgres, QueryBuilder, postgres::PgPoolOptions}; +use std::fmt::Display; +use tokio::sync::mpsc::UnboundedSender; +use tracing::{debug, trace}; + +#[derive(Clone)] +pub struct DbConnector { + pub name: String, + pub db_pool: Pool, + pub insertion_chunk_size: usize, + pub request_sender: UnboundedSender>, +} + +impl DbConnector { + pub async fn connect( + db_config: &DatabaseConfig, + index: usize, + request_sender: UnboundedSender>, + ) -> anyhow::Result { + let url = &db_config.urls[index]; + let name = url + .split('@') + .next_back() + .unwrap_or(&format!("connector_{index}")) + .to_string(); + + debug!("Connecting to database #{index} ({name})"); + let db_pool = PgPoolOptions::new() + .max_connections(db_config.pool_size) + .acquire_timeout(db_config.connection_timeout) + .connect(url) + .await?; + debug!("Successfully connected to database #{index} ({name})"); + + Ok(Self { + name, + db_pool, + insertion_chunk_size: db_config.insertion_chunk_size, + request_sender, + }) + } + + pub async fn health_check(&self) -> anyhow::Result<()> { + sqlx::query!("SELECT 1 AS health") + .fetch_one(&self.db_pool) + .await?; + Ok(()) + } + + pub async fn clear_tables(&self) -> anyhow::Result<()> { + debug!("Clearing database tables for connector {}", self.name); + + sqlx::query!("DELETE FROM public_decryption_requests") + .execute(&self.db_pool) + .await?; + sqlx::query!("DELETE FROM user_decryption_requests") + .execute(&self.db_pool) + .await?; + sqlx::query!("DELETE FROM public_decryption_responses") + .execute(&self.db_pool) + .await?; + sqlx::query!("DELETE FROM user_decryption_responses") + .execute(&self.db_pool) + .await?; + + debug!("Database tables cleared for connector {}", self.name); + Ok(()) + } + + #[tracing::instrument(fields(self = %self))] + pub async fn insert_requests(&self, requests: Vec) -> anyhow::Result<()> { + let mut public_decryptions = vec![]; + let mut user_decryptions = vec![]; + + for request in requests { + match request { + DecryptionRequest::Public(r) => public_decryptions.push(r), + DecryptionRequest::User(r) => user_decryptions.push(r), + } + } + + let mut inserted_requests = vec![]; + if !public_decryptions.is_empty() { + inserted_requests.extend(self.insert_public_requests(public_decryptions).await?); + } + if !user_decryptions.is_empty() { + inserted_requests.extend(self.insert_user_requests(user_decryptions).await?); + } + self.request_sender.send(inserted_requests)?; + + Ok(()) + } + + async fn insert_public_requests( + &self, + requests: Vec, + ) -> anyhow::Result> { + let mut requests_metadata = vec![]; + + for reqs in requests.chunks(self.insertion_chunk_size) { + let mut query_builder = QueryBuilder::new( + "INSERT INTO public_decryption_requests(decryption_id, sns_ct_materials, extra_data) ", + ); + query_builder.push_values(reqs, |mut bind, req| { + bind.push_bind(req.decryptionId.to_le_bytes_vec()) + .push_bind( + req.snsCtMaterials + .iter() + .map(SnsCiphertextMaterialDbItem::from) + .collect::>(), + ) + .push_bind(req.extraData.to_vec()); + }); + query_builder.push(" RETURNING decryption_id, created_at"); + + let query = query_builder.build(); + let query_results = self.db_pool.fetch_all(query).await?; + requests_metadata.extend(query_results.into_iter().map(|r| r.into())); + } + + trace!( + "Inserted {} public decryption requests", + requests_metadata.len(), + ); + Ok(requests_metadata) + } + + async fn insert_user_requests( + &self, + requests: Vec, + ) -> anyhow::Result> { + let mut requests_metadata = vec![]; + + for reqs in requests.chunks(self.insertion_chunk_size) { + let mut query_builder = QueryBuilder::new(" + INSERT INTO user_decryption_requests(decryption_id, sns_ct_materials, user_address, public_key, extra_data) + "); + query_builder.push_values(reqs, |mut bind, req| { + bind.push_bind(req.decryptionId.to_le_bytes_vec()) + .push_bind( + req.snsCtMaterials + .iter() + .map(SnsCiphertextMaterialDbItem::from) + .collect::>(), + ) + .push_bind(req.userAddress.to_vec()) + .push_bind(req.publicKey.to_vec()) + .push_bind(req.extraData.to_vec()); + }); + query_builder.push(" RETURNING decryption_id, created_at"); + + let query = query_builder.build(); + let query_results = self.db_pool.fetch_all(query).await?; + requests_metadata.extend(query_results.into_iter().map(|r| r.into())); + } + + trace!( + "Inserted {} user decryption requests", + requests_metadata.len(), + ); + Ok(requests_metadata) + } +} + +impl Display for DbConnector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "DbConnector {}", self.name) + } +} diff --git a/test-suite/gateway-stress/src/db/manager.rs b/test-suite/gateway-stress/src/db/manager.rs new file mode 100644 index 000000000..c67a337b6 --- /dev/null +++ b/test-suite/gateway-stress/src/db/manager.rs @@ -0,0 +1,290 @@ +use crate::{ + bench::{BenchAverageResult, BenchBurstResult, BenchRecordInput}, + cli::{DbBenchmarkArgs, DbTestArgs}, + config::Config, + db::{DbConnector, RequestBuilder, ResponseTracker}, + decryption::{BurstResult, types::DecryptionRequest}, +}; +use anyhow::anyhow; +use std::sync::Arc; +use tokio::{ + sync::{Mutex, mpsc}, + task::JoinSet, + time::{Instant, interval}, +}; +use tracing::{Instrument, error, info}; + +pub struct DatabaseTestManager { + config: Config, + db_connectors: Vec, + request_builder: RequestBuilder, + response_trackers: Vec>>, +} + +impl DatabaseTestManager { + pub async fn connect(config: Config) -> anyhow::Result { + let db_config = config + .database + .as_ref() + .ok_or_else(|| anyhow!("Missing [database] section in config file"))?; + + info!( + "Connecting to {} connectors' database...", + db_config.urls.len() + ); + + let mut db_connectors = vec![]; + let mut response_trackers = vec![]; + for i in 0..db_config.urls.len() { + let (request_sender, request_receiver) = mpsc::unbounded_channel(); + let connector = DbConnector::connect(db_config, i, request_sender).await?; + let tracker = ResponseTracker::new( + connector.name.clone(), + request_receiver, + connector.db_pool.clone(), + ); + db_connectors.push(connector); + response_trackers.push(Arc::new(Mutex::new(tracker))); + } + + let request_builder = RequestBuilder::new( + config.user_ct.clone(), + config.public_ct.clone(), + db_config.key_id, + db_config.copro_tx_sender_addr, + ); + let manager = DatabaseTestManager { + config, + db_connectors, + request_builder, + response_trackers, + }; + + manager.health_check().await?; + info!("All databases health check were successful!"); + + Ok(manager) + } + + pub async fn health_check(&self) -> anyhow::Result<()> { + let mut health_results = vec![]; + for db_connector in &self.db_connectors { + health_results.push(db_connector.health_check().await); + } + if health_results.iter().any(anyhow::Result::is_err) { + return Err(anyhow!("Health check failed: {health_results:?}")); + } + Ok(()) + } + + pub async fn clear_databases(&self) -> anyhow::Result<()> { + info!("Clearing database tables..."); + + let mut clear_results = vec![]; + for db_connector in &self.db_connectors { + clear_results.push(db_connector.clear_tables().await); + } + + if clear_results.iter().any(anyhow::Result::is_err) { + return Err(anyhow!("Database clear failed: {clear_results:?}")); + } + + info!("All databases tables were cleared successfully!"); + Ok(()) + } + + /// Runs a decryption stress testing session via the KMS Connectors' DBs. + pub async fn stress_test(mut self, args: DbTestArgs) -> anyhow::Result<()> { + if !args.skip_clear_db { + self.clear_databases().await?; + } + + let session_start = Instant::now(); + let mut interval = interval(self.config.tests_interval); + let mut burst_tasks = JoinSet::new(); + let mut burst_index = 1; + loop { + if !self.config.sequential { + interval.tick().await; + } + + if session_start.elapsed() > self.config.tests_duration { + break; + } + + let requests = self + .request_builder + .build_requests(args.decryption_type, self.config.parallel_requests)?; + burst_tasks.spawn(handle_burst( + burst_index, + self.db_connectors.clone(), + requests, + self.response_trackers.clone(), + )); + + burst_index += 1; + + if self.config.sequential { + burst_tasks.join_next().await; + } + } + + burst_tasks.join_all().await; + let elapsed = session_start.elapsed().as_secs_f64(); + info!( + "Handled all burst in {:.2}s. Throughput: {:.2} tps", + elapsed, + (self.config.parallel_requests * (burst_index - 1) as u32) as f64 / elapsed + ); + + if !args.skip_clear_db { + self.clear_databases().await?; + } + Ok(()) + } + + /// Runs a decryption benchmark session via the KMS Connectors' DBs. + pub async fn decryption_benchmark(mut self, args: DbBenchmarkArgs) -> anyhow::Result<()> { + if !args.skip_clear_db { + self.clear_databases().await?; + } + + let mut csv_reader = csv::ReaderBuilder::new() + .delimiter(b';') + .comment(Some(b'#')) + .from_path(args.input)?; + let mut average_results_writer = csv::WriterBuilder::new() + .delimiter(b';') + .from_path(&args.output)?; + let mut full_results_writer = if let Some(path) = args.results { + Some(csv::WriterBuilder::new().delimiter(b';').from_path(path)?) + } else { + None + }; + + let mut burst_index = 1; + for csv_row in csv_reader.deserialize::() { + let bench_record = csv_row.map_err(|e| anyhow!("Invalid row: {e}"))?; + info!("Starting benchmark with parameters: {bench_record:?}"); + + let results = self + .perform_single_bench(&bench_record, &mut burst_index) + .await?; + + if let Some(w) = &mut full_results_writer { + for result in results.iter() { + w.serialize(result)?; + } + } + let bench_result = BenchAverageResult::new(bench_record, results); + average_results_writer.serialize(bench_result)?; + } + + if !args.skip_clear_db { + self.clear_databases().await?; + } + Ok(()) + } + + async fn perform_single_bench( + &mut self, + bench_record: &BenchRecordInput, + burst_index: &mut usize, + ) -> anyhow::Result> { + let mut results = vec![]; + for _ in 0..bench_record.number_of_measures { + let requests = self + .request_builder + .build_requests(bench_record.decryption_type, bench_record.parallel_requests)?; + + let burst_result = handle_burst( + *burst_index, + self.db_connectors.clone(), + requests, + self.response_trackers.clone(), + ) + .await; + + if let Ok(burst_result) = burst_result { + results.push(BenchBurstResult::new( + *burst_index, + bench_record.parallel_requests, + bench_record.decryption_type, + burst_result, + )); + } + *burst_index += 1; + } + + Ok(results) + } +} + +#[tracing::instrument(skip(db_connectors, requests, response_trackers))] +async fn handle_burst( + burst_index: usize, + db_connectors: Vec, + requests: Vec, + response_trackers: Vec>>, +) -> anyhow::Result { + info!( + "Starting requests burst ({})...", + requests.first().map(|r| r.type_str()).unwrap() + ); + + insert_requests_in_all_connectors(db_connectors, requests).await; + let mut wait_responses_tasks = JoinSet::new(); + for tracker in response_trackers { + wait_responses_tasks.spawn( + async move { tracker.lock().await.wait_responses_of_next_burst().await } + .in_current_span(), + ); + } + + let results = wait_responses_tasks.join_all().await; + let mut is_error = false; + for res in results.iter() { + if let Err(e) = res { + error!("One of the connector failed to handled the burst: {e}"); + is_error = true + } + } + if is_error { + return Err(anyhow!( + "At least one connector failed to handle the burst." + )); + } + + let mut latency = 0_f64; + let mut throughput = f64::MAX; + for res in results.into_iter().map(|r| r.unwrap()) { + latency = latency.max(res.latency); + throughput = throughput.min(res.throughput); + } + + info!( + latency = latency, + throughput = throughput, + "Burst successfully processed by all connectors!", + ); + Ok(BurstResult { + latency, + throughput, + }) +} + +async fn insert_requests_in_all_connectors( + db_connectors: Vec, + requests: Vec, +) { + let mut requests_insertion_tasks = JoinSet::new(); + for connector in db_connectors { + let cloned_requests = requests.clone(); + requests_insertion_tasks.spawn(async move { + if let Err(e) = connector.insert_requests(cloned_requests).await { + error!(connector_name = connector.name, "{e}"); + } + }); + } + requests_insertion_tasks.join_all().await; +} diff --git a/test-suite/gateway-stress/src/db/mod.rs b/test-suite/gateway-stress/src/db/mod.rs new file mode 100644 index 000000000..30186f528 --- /dev/null +++ b/test-suite/gateway-stress/src/db/mod.rs @@ -0,0 +1,9 @@ +pub mod connector; +pub mod manager; +pub mod request_builder; +pub mod response_tracker; +pub mod types; + +pub use connector::DbConnector; +pub use request_builder::RequestBuilder; +pub use response_tracker::ResponseTracker; diff --git a/test-suite/gateway-stress/src/db/request_builder.rs b/test-suite/gateway-stress/src/db/request_builder.rs new file mode 100644 index 000000000..1c11898b4 --- /dev/null +++ b/test-suite/gateway-stress/src/db/request_builder.rs @@ -0,0 +1,109 @@ +use crate::{ + config::CiphertextConfig, + decryption::types::{DecryptionRequest, DecryptionType}, +}; +use alloy::primitives::{Address, Bytes, U256}; +use fhevm_gateway_bindings::decryption::Decryption::{ + PublicDecryptionRequest, SnsCiphertextMaterial, UserDecryptionRequest, +}; +use rand::Rng; + +pub struct RequestBuilder { + id_counter: U256, + user_ct: Vec, + public_ct: Vec, + key_id: U256, + copro_tx_sender_addr: Address, +} + +impl RequestBuilder { + pub fn new( + user_ct: Vec, + public_ct: Vec, + key_id: U256, + copro_tx_sender_addr: Address, + ) -> Self { + Self { + // Take a high value for the id_counter to avoid polluting id that could be used in + // the testing environment + id_counter: (U256::MAX / U256::from(4)) * U256::from(3), + user_ct, + public_ct, + key_id, + copro_tx_sender_addr, + } + } + + pub fn build_requests( + &mut self, + decryption_type: DecryptionType, + count: u32, + ) -> anyhow::Result> { + let mut requests = Vec::new(); + + for _ in 0..count { + let request = match decryption_type { + DecryptionType::Public => DecryptionRequest::Public(self.build_public_request()?), + DecryptionType::User => DecryptionRequest::User(self.build_user_request()?), + }; + requests.push(request); + } + + Ok(requests) + } + + fn build_public_request(&mut self) -> anyhow::Result { + let decryption_id = self.generate_unique_id(); + let sns_ct_materials = self.generate_sns_materials(self.public_ct.clone()); + let extra_data = self.generate_extra_data(); + + Ok(PublicDecryptionRequest { + decryptionId: decryption_id, + snsCtMaterials: sns_ct_materials, + extraData: extra_data, + }) + } + + fn build_user_request(&mut self) -> anyhow::Result { + let decryption_id = self.generate_unique_id(); + let sns_ct_materials = self.generate_sns_materials(self.user_ct.clone()); + let user_address = Address::from(rand::rng().random::<[u8; 20]>()); + let public_key = alloy::hex::decode(COMMON_PUBLIC_KEY).unwrap().into(); + let extra_data = self.generate_extra_data(); + + Ok(UserDecryptionRequest { + decryptionId: decryption_id, + snsCtMaterials: sns_ct_materials, + userAddress: user_address, + publicKey: public_key, + extraData: extra_data, + }) + } + + fn generate_unique_id(&mut self) -> U256 { + let id = self.id_counter; + self.id_counter += U256::ONE; + id + } + + fn generate_sns_materials( + &self, + ciphertexts: Vec, + ) -> Vec { + ciphertexts + .iter() + .map(|ct| SnsCiphertextMaterial { + ctHandle: ct.handle, + keyId: self.key_id, + snsCiphertextDigest: ct.digest, + coprocessorTxSenderAddresses: vec![self.copro_tx_sender_addr], + }) + .collect() + } + + fn generate_extra_data(&self) -> Bytes { + vec![1].into() + } +} + +const COMMON_PUBLIC_KEY: &str = "0x0300000000000000302e35000000000300000000000000302e311300000000000000556e69666965645075626c6963456e634b6579000000000000000020030000000000003098880dd1a4a1277fb90149353c7a655c4552799d3bba834f4a7fd8eb99e89a5f1c3b37e99826250954b80bcb9a1ac2745159a300c2f86c4b1f03d048008bf9f186e288901e70362937804632537810140a6a44eb924a87771c2aea757cf72fd49670dbc8a530889ad2fa8fb59c1deb651bc7527b679175cd487caad286183610db0357b6c8a8adda92a1b008d47063de88ab17ccc536083841a394422bb90b3297f36417e127967df3ca7035c88a905a9f8842e106bfc11210e02ab309f07e91103f092067257aae444197236006edf68df47a8d9f9b42e0a998806849646a7c4a42c375102f2b1c5185e03e74387e69b20b40c60863526a0f8a93ad1823ab89ae59dc141d7c1078a1086b52b7ecf26a2c200f49b5125593b5bdb76de0a635fb298036700c162770a45b9029998775a04bd41b73884072c00688ba83177f440d6b0c79c7895c9a6c4230b891a47027cba42ed220c89a904ad20862d546621162268124092b5b4aee603b6fe74c3c7451143ba0ce847aae70a36bd76df123440d866e6029cf1e1aa9f2b5c66255c54abc023b769ba7c8990742b8961687a286a643079224c979f0da6a63a34581fab6e9d0c2f645c78dcc93c7423f2aa10e5af98f4e28aafc1186ddf79eff04a84d484577369bccc3802cd47dcbc4772947cd6c2555453a0a8fc87b83932a0c6c4dd395677797105987967909bb6a754e5af9c1775b07cbc63410ac6c51e6719d7b645e667c08e842ee5092f3e74dcd271a8bf764e9341dcdf53ddd63895da61249a6a576886f76778c4cb636acd32be88529ceea5c9fb55c91c5549ba04c6e2b52f2d08a8803c64587c11393479b0065c3114f34fc2ed81c4ae0a67574009b04094ba2429f47b0278c14c07474aa3b1404abc11f63a595d55599ca054c9c5c74ea91a8624aac2527a1c2f7a7e48744c3a94ee3908c0beb944e051062ab33c8145d85023abf1b79717946e6ca35ba56785cb5abf4d7434536c3f9b0329a3b95efc7ae0f856be77937c2335c53ab9540c44792e8288b0c3c970c3762011d39cb35a9ac7016a69e33f78096b66c00d8486db5c447e0e5f87ae1ddb55b6e2404de14510f3840e3077a6d8d0b1385aa801137f986"; diff --git a/test-suite/gateway-stress/src/db/response_tracker.rs b/test-suite/gateway-stress/src/db/response_tracker.rs new file mode 100644 index 000000000..a1a78e7c3 --- /dev/null +++ b/test-suite/gateway-stress/src/db/response_tracker.rs @@ -0,0 +1,119 @@ +use crate::{ + db::types::{DecryptionRequestDbMetadata, DecryptionResponseDbMetadata}, + decryption::BurstResult, +}; +use alloy::primitives::U256; +use anyhow::anyhow; +use sqlx::{Pool, Postgres}; +use std::{fmt::Display, time::Duration}; +use tokio::{sync::mpsc::UnboundedReceiver, time::interval}; +use tracing::{debug, trace}; + +const RESPONSE_POLLING: Duration = Duration::from_millis(500); + +pub struct ResponseTracker { + name: String, + request_receiver: UnboundedReceiver>, + db_pool: Pool, +} + +impl ResponseTracker { + pub fn new( + name: String, + request_receiver: UnboundedReceiver>, + db_pool: Pool, + ) -> Self { + Self { + name, + request_receiver, + db_pool, + } + } + + #[tracing::instrument(fields(self = %self))] + pub async fn wait_responses_of_next_burst(&mut self) -> anyhow::Result { + let requests = match self.request_receiver.recv().await { + None => return Err(anyhow!("Request receiver channel was closed unexpectedly")), + Some(requests) => requests, + }; + + let mut responses = Vec::new(); + if requests.is_empty() { + return Err(anyhow!("Empty request burst")); + } + + let burst_start = requests.iter().map(|r| r.created_at).min().unwrap(); + let mut interval = interval(RESPONSE_POLLING); + let mut ids_to_process: Vec> = requests + .into_iter() + .map(|r| r.id.to_le_bytes_vec()) + .collect(); + + while !ids_to_process.is_empty() { + interval.tick().await; + + let resps = self.fetch_responses_with_ids(&ids_to_process).await?; + ids_to_process.retain(|id| !resps.iter().any(|r| r.id.to_le_bytes_vec() == *id)); + + responses.extend(resps); + } + + let burst_end = responses.iter().map(|r| r.created_at).max().unwrap(); + let latency = (burst_end - burst_start).as_seconds_f64(); + + let result = BurstResult { + latency, + throughput: responses.len() as f64 / latency, + }; + debug!( + latency = result.latency, + throughput = result.throughput, + "Burst successfully processed!" + ); + + Ok(result) + } + + async fn fetch_responses_with_ids( + &self, + ids: &[Vec], + ) -> anyhow::Result> { + trace!("Fetching responses for {} ids", ids.len()); + + let mut responses = Vec::new(); + let public_rows = sqlx::query!( + "SELECT decryption_id, created_at FROM public_decryption_responses WHERE decryption_id = ANY($1::bytea[])", + ids, + ) + .fetch_all(&self.db_pool) + .await?; + for row in public_rows { + responses.push(DecryptionResponseDbMetadata { + id: U256::from_le_slice(&row.decryption_id), + created_at: row.created_at, + }); + } + + let user_rows = sqlx::query!( + "SELECT decryption_id, created_at FROM user_decryption_responses WHERE decryption_id = ANY($1::bytea[])", + ids, + ) + .fetch_all(&self.db_pool) + .await?; + for row in user_rows { + responses.push(DecryptionResponseDbMetadata { + id: U256::from_le_slice(&row.decryption_id), + created_at: row.created_at, + }); + } + + trace!("Fetched {} responses", responses.len()); + Ok(responses) + } +} + +impl Display for ResponseTracker { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ResponseTracker {}", self.name) + } +} diff --git a/test-suite/gateway-stress/src/db/types.rs b/test-suite/gateway-stress/src/db/types.rs new file mode 100644 index 000000000..d72ac24dd --- /dev/null +++ b/test-suite/gateway-stress/src/db/types.rs @@ -0,0 +1,66 @@ +use alloy::primitives::{Address, U256}; +use fhevm_gateway_bindings::decryption::Decryption::SnsCiphertextMaterial; +use sqlx::{Row, postgres::PgRow, types::time::PrimitiveDateTime}; + +// Copy-pasted from kms-connector's code +// Ideally, we would use `connector-utils` as a dependency crate to avoid duplication. +// But as both `kms` and `kms-connector` used pinned dependencies, they are conflicting and cannot +// be both used by the `gateway-stress` tool + +#[derive(sqlx::Type, Clone, Debug, Default, PartialEq)] +#[sqlx(type_name = "sns_ciphertext_material")] +pub struct SnsCiphertextMaterialDbItem { + ct_handle: [u8; 32], + key_id: [u8; 32], + sns_ciphertext_digest: [u8; 32], + coprocessor_tx_sender_addresses: Vec<[u8; 20]>, +} + +impl From<&SnsCiphertextMaterial> for SnsCiphertextMaterialDbItem { + fn from(value: &SnsCiphertextMaterial) -> Self { + Self { + ct_handle: *value.ctHandle, + key_id: value.keyId.to_le_bytes(), + sns_ciphertext_digest: *value.snsCiphertextDigest, + coprocessor_tx_sender_addresses: value + .coprocessorTxSenderAddresses + .iter() + .map(|a| *a.0) + .collect(), + } + } +} + +impl From<&SnsCiphertextMaterialDbItem> for SnsCiphertextMaterial { + fn from(value: &SnsCiphertextMaterialDbItem) -> Self { + Self { + ctHandle: value.ct_handle.into(), + keyId: U256::from_le_bytes(value.key_id), + snsCiphertextDigest: value.sns_ciphertext_digest.into(), + coprocessorTxSenderAddresses: value + .coprocessor_tx_sender_addresses + .iter() + .map(Address::from) + .collect(), + } + } +} + +pub struct DecryptionRequestDbMetadata { + pub id: U256, + pub created_at: PrimitiveDateTime, +} + +pub struct DecryptionResponseDbMetadata { + pub id: U256, + pub created_at: PrimitiveDateTime, +} + +impl From for DecryptionRequestDbMetadata { + fn from(row: PgRow) -> Self { + Self { + id: U256::from_le_slice(row.get("decryption_id")), + created_at: row.get("created_at"), + } + } +} diff --git a/test-suite/gateway-stress/src/decryption/mod.rs b/test-suite/gateway-stress/src/decryption/mod.rs index 3886285d9..070a91e75 100644 --- a/test-suite/gateway-stress/src/decryption/mod.rs +++ b/test-suite/gateway-stress/src/decryption/mod.rs @@ -1,8 +1,8 @@ -mod public; +pub mod public; +pub mod types; pub mod user; pub use public::{init_public_decryption_response_listener, public_decryption_burst}; -use std::time::Duration; pub use user::{init_user_decryption_response_listener, user_decryption_burst}; use alloy::{ @@ -11,9 +11,12 @@ use alloy::{ rpc::types::{TransactionReceipt, TransactionRequest}, }; use anyhow::anyhow; +use std::time::Duration; use tokio::sync::mpsc::UnboundedSender; use tracing::{debug, trace, warn}; +use crate::blockchain::manager::AppProvider; + pub const EVENT_LISTENER_POLLING: Duration = Duration::from_millis(500); fn extract_id_from_receipt( @@ -46,29 +49,25 @@ where const TX_RETRIES: usize = 50; const TX_GAS_INCREASE_PERCENT: u128 = 105; -async fn send_tx_with_retries( - provider: &P, +async fn send_tx_with_retries( + provider: &AppProvider, mut decryption_call: TransactionRequest, id_sender: UnboundedSender, extract_id_fn: F, ) -> Result<(), anyhow::Error> where F: Fn(&TransactionReceipt) -> anyhow::Result, - P: Provider, { let mut last_error = String::new(); for i in 1..=TX_RETRIES { overprovision_gas(provider, &mut decryption_call).await; trace!("Sending transaction to the Gateway"); - match provider.send_transaction(decryption_call.clone()).await { - Ok(decryption_tx) => { - debug!("Transaction has been sent to the Gateway"); - let receipt = decryption_tx - .get_receipt() - .await - .map_err(|e| anyhow!("Failed to get receipt: {e}"))?; - + match provider + .send_transaction_sync(decryption_call.clone()) + .await + { + Ok(receipt) => { let id = extract_id_fn(&receipt)?; id_sender.send(id)?; return Ok(()); diff --git a/test-suite/gateway-stress/src/decryption/public.rs b/test-suite/gateway-stress/src/decryption/public.rs index 313defbd4..3c508dc7e 100644 --- a/test-suite/gateway-stress/src/decryption/public.rs +++ b/test-suite/gateway-stress/src/decryption/public.rs @@ -1,4 +1,5 @@ use crate::{ + blockchain::manager::AppProvider, config::Config, decryption::{ BurstResult, EVENT_LISTENER_POLLING, extract_id_from_receipt, send_tx_with_retries, @@ -30,6 +31,8 @@ use tokio::{ }; use tracing::{Instrument, debug, error, trace}; +pub type PublicDecryptThresholdEvent = sol_types::Result<(PublicDecryptionResponse, Log)>; + /// Sends a burst of PublicDecryptionRequest. #[tracing::instrument(skip( config, @@ -38,16 +41,15 @@ use tracing::{Instrument, debug, error, trace}; requests_pb, responses_pb ))] -pub async fn public_decryption_burst( +pub async fn public_decryption_burst( burst_index: usize, config: Config, - decryption_contract: DecryptionInstance

, + decryption_contract: DecryptionInstance, response_listener: Arc>, requests_pb: ProgressBar, responses_pb: ProgressBar, ) -> anyhow::Result where - P: Provider + Clone + 'static, S: Stream> + Unpin + Send + 'static, { debug!("Start of the burst..."); @@ -69,7 +71,7 @@ where send_public_decryption( index, decryption_contract.clone(), - config.public_ct_handles.clone(), + config.public_ct.iter().map(|ct| ct.handle).collect(), id_sender.clone(), ) .in_current_span(), @@ -94,9 +96,9 @@ where /// Sends a PublicDecryptionRequest transaction to the Gateway. #[tracing::instrument(skip(decryption_contract, handles, id_sender))] -async fn send_public_decryption( +async fn send_public_decryption( index: u32, - decryption_contract: DecryptionInstance

, + decryption_contract: DecryptionInstance, handles: Vec>, id_sender: UnboundedSender, ) { @@ -105,8 +107,8 @@ async fn send_public_decryption( } } -async fn send_public_decryption_inner( - decryption_contract: DecryptionInstance

, +async fn send_public_decryption_inner( + decryption_contract: DecryptionInstance, handles: Vec>, id_sender: UnboundedSender, ) -> anyhow::Result<()> { diff --git a/test-suite/gateway-stress/src/decryption/types.rs b/test-suite/gateway-stress/src/decryption/types.rs new file mode 100644 index 000000000..ddb320843 --- /dev/null +++ b/test-suite/gateway-stress/src/decryption/types.rs @@ -0,0 +1,60 @@ +use std::str::FromStr; + +use anyhow::anyhow; +use fhevm_gateway_bindings::decryption::Decryption::{ + PublicDecryptionRequest, UserDecryptionRequest, +}; +use serde::{Deserialize, Deserializer, Serializer}; + +#[derive(Debug, Clone)] +pub enum DecryptionRequest { + Public(PublicDecryptionRequest), + User(UserDecryptionRequest), +} + +#[derive(Copy, Clone, Debug)] +pub enum DecryptionType { + Public, + User, +} + +impl FromStr for DecryptionType { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + if s == "u" || s.starts_with("user") { + Ok(DecryptionType::User) + } else if s == "p" || s.starts_with("public") { + Ok(DecryptionType::Public) + } else { + Err(anyhow!("Invalid decryption type")) + } + } +} + +pub fn decryption_type_from_str<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?.to_lowercase(); + s.parse().map_err(serde::de::Error::custom) +} + +pub fn decryption_type_serialize(d: &DecryptionType, s: S) -> Result +where + S: Serializer, +{ + match d { + DecryptionType::Public => s.serialize_str("public"), + DecryptionType::User => s.serialize_str("user"), + } +} + +impl DecryptionRequest { + pub fn type_str(&self) -> String { + match self { + DecryptionRequest::Public(_) => "PublicDecryptionRequest".to_string(), + DecryptionRequest::User(_) => "UserDecryptionRequest".to_string(), + } + } +} diff --git a/test-suite/gateway-stress/src/decryption/user.rs b/test-suite/gateway-stress/src/decryption/user.rs index 678670c32..84a7eb68c 100644 --- a/test-suite/gateway-stress/src/decryption/user.rs +++ b/test-suite/gateway-stress/src/decryption/user.rs @@ -1,4 +1,5 @@ use crate::{ + blockchain::manager::AppProvider, config::Config, decryption::{ BurstResult, EVENT_LISTENER_POLLING, extract_id_from_receipt, send_tx_with_retries, @@ -13,7 +14,9 @@ use alloy::{ }; use anyhow::anyhow; use fhevm_gateway_bindings::decryption::{ - Decryption::{self, CtHandleContractPair, DecryptionInstance, UserDecryptionResponse}, + Decryption::{ + self, CtHandleContractPair, DecryptionInstance, UserDecryptionResponseThresholdReached, + }, IDecryption::{ContractsInfo, RequestValidity}, }; use futures::{Stream, StreamExt}; @@ -34,6 +37,9 @@ use tokio::{ }; use tracing::{Instrument, debug, error, trace}; +pub type UserDecryptThresholdEvent = + sol_types::Result<(UserDecryptionResponseThresholdReached, Log)>; + /// Sends a burst of UserDecryptionRequest. #[allow(clippy::too_many_arguments)] #[tracing::instrument(skip( @@ -45,10 +51,10 @@ use tracing::{Instrument, debug, error, trace}; requests_pb, responses_pb ))] -pub async fn user_decryption_burst( +pub async fn user_decryption_burst( burst_index: usize, config: Config, - decryption_contract: DecryptionInstance

, + decryption_contract: DecryptionInstance, sdk: Arc, user_addr: Address, response_listener: Arc>, @@ -56,8 +62,7 @@ pub async fn user_decryption_burst( responses_pb: ProgressBar, ) -> anyhow::Result where - P: Provider + Clone + 'static, - S: Stream> + Unpin + Send + 'static, + S: Stream + Unpin + Send + 'static, { debug!("Start of the burst..."); let (id_sender, id_receiver) = mpsc::unbounded_channel(); @@ -105,9 +110,9 @@ where /// Sends a UserDecryptionRequest transaction to the Gateway. #[tracing::instrument(skip(decryption_contract, user_addr, sdk, config, id_sender))] -async fn send_user_decryption( +async fn send_user_decryption( index: u32, - decryption_contract: DecryptionInstance

, + decryption_contract: DecryptionInstance, user_addr: Address, sdk: Arc, config: Config, @@ -120,25 +125,35 @@ async fn send_user_decryption( } } -async fn send_user_decryption_inner( - decryption_contract: DecryptionInstance

, +async fn send_user_decryption_inner( + decryption_contract: DecryptionInstance, user_addr: Address, sdk: Arc, config: Config, id_sender: UnboundedSender, ) -> anyhow::Result<()> { + let blockchain_config = config + .blockchain + .as_ref() + .expect("Missing [blockchain] section in config file"); // Should be unreachable + let timestamp = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH)? .as_secs(); - let eip712 = generate_eip712(sdk, &config, timestamp)?; + let eip712 = generate_eip712( + sdk, + config.allowed_contract, + blockchain_config.private_key.clone(), + timestamp, + )?; let decryption_call = decryption_contract .userDecryptionRequest( config - .user_ct_handles + .user_ct .iter() - .map(|h| CtHandleContractPair { - ctHandle: *h, + .map(|ct| CtHandleContractPair { + ctHandle: ct.handle, contractAddress: config.allowed_contract, }) .collect(), @@ -147,7 +162,7 @@ async fn send_user_decryption_inner( durationDays: U256::from(DURATION_DAYS), }, ContractsInfo { - chainId: U256::from(config.host_chain_id), + chainId: U256::from(blockchain_config.host_chain_id), addresses: vec![config.allowed_contract], }, user_addr, @@ -184,18 +199,23 @@ pub async fn init_user_decryption_response_listener( ) -> anyhow::Result< Arc< Mutex< - impl Stream> + Unpin + Send, + impl Stream> + + Unpin + + Send, >, >, > { - debug!("Subcribing to UserDecryptionResponse events..."); + debug!("Subcribing to UserDecryptionResponseThresholdReached events..."); let mut response_filter = decryption_contract - .UserDecryptionResponse_filter() + .UserDecryptionResponseThresholdReached_filter() .watch() .await - .map_err(|e| anyhow!("Failed to subscribe to UserDecryptionResponse {e}"))?; + .map_err(|e| { + anyhow!("Failed to subscribe to UserDecryptionResponseThresholdReached {e}") + })?; debug!( - "Subcribed to UserDecryptionResponse events! Can start sending UserDecryptionRequests..." + "Subcribed to UserDecryptionResponseThresholdReached events! \ + Can start sending UserDecryptionRequests..." ); response_filter.poller = response_filter @@ -207,12 +227,10 @@ pub async fn init_user_decryption_response_listener( pub fn generate_eip712( sdk: Arc, - config: &Config, + allowed_contract: Address, + private_key: String, timestamp: u64, ) -> anyhow::Result { - let allowed_contract = config.allowed_contract; - let private_key = config.private_key.clone().unwrap(); - // Spawn in new thread otherwise panic because it blocks the async runtime std::thread::spawn(move || { sdk.create_eip712_signature_builder() @@ -239,7 +257,7 @@ async fn wait_for_burst_responses( progress_bar: ProgressBar, ) -> anyhow::Result where - S: Stream> + Unpin, + S: Stream + Unpin, { let burst_start = Instant::now(); @@ -252,13 +270,16 @@ where )); }; - trace!("UserDecryptionRequest #{id} was sent. Waiting for UserDecryptionResponse #{id}..."); + trace!( + "UserDecryptionRequest #{id} was sent. \ + Waiting for UserDecryptionResponseThresholdReached #{id}..." + ); while !received_id_guard.remove(&id) { match listener_guard.next().await { Some(Ok((response, _))) => { let response_id = response.decryptionId; - trace!("Received UserDecryptionResponse #{response_id}"); + trace!("Received UserDecryptionResponseThresholdReached #{response_id}"); received_id_guard.insert(response_id); progress_bar.inc(1); } @@ -266,7 +287,7 @@ where None => return Err(anyhow!("No more events to receive!")), } } - debug!("UserDecryptionResponse #{id} was successfully received!"); + debug!("UserDecryptionResponseThresholdReached #{id} was successfully received!"); } drop(received_id_guard); drop(listener_guard); diff --git a/test-suite/gateway-stress/src/main.rs b/test-suite/gateway-stress/src/main.rs index 0b660288f..966fcd81a 100644 --- a/test-suite/gateway-stress/src/main.rs +++ b/test-suite/gateway-stress/src/main.rs @@ -1,18 +1,19 @@ -mod app; mod bench; mod blockchain; mod cli; mod config; +mod db; mod decryption; use crate::{ - app::App, + blockchain::GatewayTestManager, cli::{Cli, Subcommands}, config::Config, + db::manager::DatabaseTestManager, }; use clap::Parser; use std::process::ExitCode; -use tracing::{error, info}; +use tracing::{debug, error}; use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -27,26 +28,45 @@ async fn main() -> ExitCode { async fn run() -> anyhow::Result<()> { let cli = Cli::parse(); - let mut config = Config::from_env_and_file(cli.config)?; + let mut config = Config::from_env_and_file(&cli.config)?; + update_config_from_cli(&mut config, &cli); - // Override some fields of the config by the CLI + debug!("Config: {config:?}"); + match cli.subcommand { + Subcommands::Gw(args) => { + let test_manager = GatewayTestManager::connect(config).await?; + test_manager.stress_test(args).await? + } + Subcommands::BenchGw(args) => { + let test_manager = GatewayTestManager::connect(config).await?; + test_manager.decryption_benchmark(args).await? + } + Subcommands::Db(args) => { + let test_manager = DatabaseTestManager::connect(config).await?; + test_manager.stress_test(args).await? + } + Subcommands::BenchDb(args) => { + let test_manager = DatabaseTestManager::connect(config).await?; + test_manager.decryption_benchmark(args).await? + } + } + + Ok(()) +} + +fn update_config_from_cli(config: &mut Config, cli: &Cli) { if cli.sequential { config.sequential = cli.sequential; } if let Some(parallel) = cli.parallel { config.parallel_requests = parallel; } - - info!("Config: {config:?}"); - let app = App::connect(config).await?; - match cli.subcommand { - Subcommands::Public => app.public_decryption_stress_test().await?, - Subcommands::User => app.user_decryption_stress_test().await?, - Subcommands::Benchmark(args) => app.decryption_benchmark(args).await?, - _ => todo!(), + if let Some(duration) = cli.duration { + config.tests_duration = duration; + } + if let Some(interval) = cli.interval { + config.tests_interval = interval; } - - Ok(()) } fn init_tracing() { diff --git a/test-suite/gateway-stress/templates/db_bench.csv b/test-suite/gateway-stress/templates/db_bench.csv new file mode 100644 index 000000000..856728c98 --- /dev/null +++ b/test-suite/gateway-stress/templates/db_bench.csv @@ -0,0 +1,15 @@ +parallel_requests;number_of_measures;decryption_type +1;50;public +10;50;public +50;50;public +100;50;public +200;50;public +500;50;public +1000;50;public +1;50;user +10;50;user +50;50;user +100;50;user +200;50;user +500;50;user +1000;50;user diff --git a/test-suite/gateway-stress/templates/bench.csv b/test-suite/gateway-stress/templates/gw_bench.csv similarity index 100% rename from test-suite/gateway-stress/templates/bench.csv rename to test-suite/gateway-stress/templates/gw_bench.csv