From 65c343f2ed6036d2ce021c6480f73848d3b2db5b Mon Sep 17 00:00:00 2001 From: armedx86 <90354404+armedx86@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:19:31 +0100 Subject: [PATCH 1/6] Implement rudimentary load tester bin that is able to read public keys from file --- Cargo.lock | 610 ++++++++++++++++++- Cargo.toml | 1 + load_tester/Cargo.toml | 31 + load_tester/src/cli.rs | 30 + load_tester/src/load_test_endpoint_params.rs | 30 + load_tester/src/main.rs | 90 +++ load_tester/src/requests.rs | 27 + 7 files changed, 806 insertions(+), 13 deletions(-) create mode 100644 load_tester/Cargo.toml create mode 100644 load_tester/src/cli.rs create mode 100644 load_tester/src/load_test_endpoint_params.rs create mode 100644 load_tester/src/main.rs create mode 100644 load_tester/src/requests.rs diff --git a/Cargo.lock b/Cargo.lock index b1e8777..685767f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,6 +63,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.11" @@ -100,6 +111,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -617,6 +634,18 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.5.1" @@ -777,6 +806,45 @@ dependencies = [ "serde", ] +[[package]] +name = "byte-unit" +version = "5.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" +dependencies = [ + "rust_decimal", + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "bytemuck" version = "1.18.0" @@ -840,6 +908,21 @@ dependencies = [ "thiserror 1.0.64", ] +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.1.30" @@ -1005,6 +1088,19 @@ dependencies = [ "unreachable", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1137,6 +1233,31 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.6.0", + "crossterm_winapi", + "libc", + "mio 0.8.11", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -1459,6 +1580,16 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -1472,6 +1603,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1549,6 +1693,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1579,6 +1729,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -1678,6 +1834,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1809,6 +1974,26 @@ dependencies = [ "scroll", ] +[[package]] +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.8.5", + "smallvec", + "spinning_top", +] + [[package]] name = "h2" version = "0.3.26" @@ -1861,6 +2046,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] [[package]] name = "hashbrown" @@ -1868,7 +2056,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.11", ] [[package]] @@ -1882,6 +2070,25 @@ name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "crossbeam-channel", + "flate2", + "nom", + "num-traits", +] [[package]] name = "heck" @@ -2333,6 +2540,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -2660,7 +2876,7 @@ dependencies = [ "clap 4.5.24", "crossbeam-channel", "dashmap", - "env_logger", + "env_logger 0.9.3", "futures", "geyser-grpc-connector", "itertools 0.10.5", @@ -2686,7 +2902,7 @@ dependencies = [ "async-trait", "bincode", "clap 4.5.24", - "env_logger", + "env_logger 0.9.3", "futures", "itertools 0.10.5", "lazy_static", @@ -2734,7 +2950,7 @@ dependencies = [ "base64 0.21.7", "bincode", "clap 4.5.24", - "env_logger", + "env_logger 0.9.3", "futures", "geyser-grpc-connector", "itertools 0.10.5", @@ -2793,6 +3009,23 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "load-tester" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "clap 4.5.24", + "env_logger 0.11.6", + "log", + "rand 0.8.5", + "reqwest 0.12.9", + "rlt", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -2809,6 +3042,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.0", +] + [[package]] name = "lz4" version = "1.28.0" @@ -2911,6 +3153,18 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.0.2" @@ -3007,6 +3261,12 @@ dependencies = [ "memoffset", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "nom" version = "7.1.3" @@ -3017,6 +3277,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -3259,6 +3525,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "papergrid" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -3616,7 +3893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" dependencies = [ "heck 0.5.0", - "itertools 0.12.1", + "itertools 0.13.0", "log", "multimap", "once_cell", @@ -3649,7 +3926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.90", @@ -3688,6 +3965,26 @@ dependencies = [ "autotools", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "qstring" version = "0.7.2" @@ -3708,6 +4005,21 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "quanta" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quic-geyser-client" version = "0.1.5" @@ -3852,6 +4164,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.7.3" @@ -3932,6 +4250,36 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "ratatui" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" +dependencies = [ + "bitflags 2.6.0", + "cassowary", + "compact_str", + "crossterm", + "itertools 0.13.0", + "lru", + "paste", + "stability", + "strum 0.26.3", + "strum_macros 0.26.4", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "raw-cpuid" +version = "11.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6928fa44c097620b706542d428957635951bade7143269085389d42c8a4927e" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "rayon" version = "1.10.0" @@ -4002,6 +4350,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.11.27" @@ -4142,6 +4499,65 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rlt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5ae299598d2facaedddff60742e225a348c90007b418c6e37de2b326cad979" +dependencies = [ + "anyhow", + "async-trait", + "byte-unit", + "cfg-if", + "clap 4.5.24", + "crossterm", + "governor", + "hdrhistogram", + "http 1.2.0", + "humantime", + "itertools 0.13.0", + "log", + "nonzero_ext", + "parking_lot", + "ratatui", + "serde", + "serde_json", + "tabled", + "tokio", + "tokio-util", + "tracing", + "tui-logger", +] + [[package]] name = "route-recognizer" version = "0.3.1" @@ -4169,6 +4585,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rust_decimal" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +dependencies = [ + "arrayvec", + "borsh 1.5.1", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -4383,6 +4815,12 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "2.11.1" @@ -4606,6 +5044,27 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio 0.8.11", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -4621,6 +5080,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "0.3.11" @@ -4918,7 +5383,7 @@ version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6a931ffe5559ad680ed95e4c146a7b790584d843e088a7d07ae7768d5cee5ba" dependencies = [ - "ahash", + "ahash 0.8.11", "lazy_static", "log", "rustc_version", @@ -4994,7 +5459,7 @@ version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e79a42aecca4cb913f52b39141fc7ce766350e46332a9df57d8f9d14661dc2c7" dependencies = [ - "env_logger", + "env_logger 0.9.3", "lazy_static", "log", ] @@ -5059,7 +5524,7 @@ version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce2d03a15b8ddcec849b6c7c701aabcc8430926719d74b6c40c574aa0e2e2a8" dependencies = [ - "ahash", + "ahash 0.8.11", "bincode", "bv", "caps", @@ -5381,8 +5846,8 @@ dependencies = [ "solana-zk-token-proof-program", "solana-zk-token-sdk", "static_assertions", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "symlink", "tar", "tempfile", @@ -5824,6 +6289,15 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.5.4" @@ -6187,6 +6661,16 @@ dependencies = [ "spl-program-error", ] +[[package]] +name = "stability" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" +dependencies = [ + "quote", + "syn 2.0.90", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -6217,7 +6701,16 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros", + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", ] [[package]] @@ -6233,6 +6726,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.90", +] + [[package]] name = "subtle" version = "2.6.1" @@ -6348,6 +6854,36 @@ dependencies = [ "libc", ] +[[package]] +name = "tabled" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e" +dependencies = [ + "papergrid", + "tabled_derive", + "unicode-width", +] + +[[package]] +name = "tabled_derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c138f99377e5d653a371cdad263615634cfc8467685dfe8e73e2b8e98f44b17" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tar" version = "0.4.42" @@ -6548,7 +7084,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.2", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -6875,6 +7411,22 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tui-logger" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd1a0f217c2180e736bc9f3282fea4af182483532c6e719081b6b1c6d6be90" +dependencies = [ + "chrono", + "fxhash", + "lazy_static", + "log", + "parking_lot", + "ratatui", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tungstenite" version = "0.20.1" @@ -6932,6 +7484,23 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "unicode-width" version = "0.1.14" @@ -7002,6 +7571,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + [[package]] name = "utf8parse" version = "0.2.2" @@ -7447,6 +8022,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x509-parser" version = "0.14.0" diff --git a/Cargo.toml b/Cargo.toml index e0de6ea..4e358bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "common", "token_account_storage", "simulate_from_snapshot", + "load_tester", ] [workspace.package] diff --git a/load_tester/Cargo.toml b/load_tester/Cargo.toml new file mode 100644 index 0000000..5bcf96e --- /dev/null +++ b/load_tester/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "load-tester" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "load-tester" +path = "src/main.rs" + +[dependencies] +# solana-rpc-client-api = { workspace = true } +# solana-rpc-client = { workspace = true } + +anyhow = { workspace = true } +async-trait = "^0.1" +clap = { version = "^4.5.4", features = ["derive"] } +env_logger = "^0.11.6" +log = { workspace = true } +rand = "^0.8.5" +reqwest = { version = "^0.12.9", features = [ + "blocking", + "brotli", + "deflate", + "gzip", + "rustls-tls", + "json", +] } +rlt = "^0.2.1" +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } diff --git a/load_tester/src/cli.rs b/load_tester/src/cli.rs new file mode 100644 index 0000000..b7729d8 --- /dev/null +++ b/load_tester/src/cli.rs @@ -0,0 +1,30 @@ +use clap::{Parser, Subcommand}; +use reqwest::Url; + +#[derive(Parser, Clone)] +pub struct LoadTesterCli { + /// Target URL + pub url: Url, + + #[command(subcommand)] + pub load_test_request: LoadTestRequestCommand, + + #[command(flatten)] + pub bench_opts: rlt::cli::BenchCli, +} + +#[derive(Debug, Clone, Subcommand)] +pub enum LoadTestRequestCommand { + /// Run benchmark for the getAccountInfo request + #[command(name = "get-account-info")] + #[group(id = "get-account-info", required = true, multiple = false)] + GetAccountInfoArgs { + /// the public key of the account + #[arg(short, long)] + pk: Option, + + /// the file to read the public keys from + #[arg(short, long)] + input_file: Option, + }, +} diff --git a/load_tester/src/load_test_endpoint_params.rs b/load_tester/src/load_test_endpoint_params.rs new file mode 100644 index 0000000..04f4c67 --- /dev/null +++ b/load_tester/src/load_test_endpoint_params.rs @@ -0,0 +1,30 @@ +use std::fs::read_to_string; + +use crate::cli::{LoadTestRequestCommand, LoadTesterCli}; + +#[derive(Debug, Clone)] +pub enum LoadTestEndpointParams { + GetAccountInfo(Vec), +} + +impl LoadTestEndpointParams { + pub fn get_params_from_cli_args(cli_args: &LoadTesterCli) -> LoadTestEndpointParams { + match &cli_args.load_test_request { + LoadTestRequestCommand::GetAccountInfoArgs { pk, input_file } => { + let pks = match (pk, input_file) { + (Some(pk), None) => vec![pk.clone()], + (None, Some(input_file)) => read_to_string(input_file) + .unwrap_or_else(|e| { + panic!("failed to read file: file={}, error={}", input_file, e) + }) + .trim() + .lines() + .map(|s| s.to_string()) + .collect(), + _ => unreachable!(), + }; + LoadTestEndpointParams::GetAccountInfo(pks) + } + } + } +} diff --git a/load_tester/src/main.rs b/load_tester/src/main.rs new file mode 100644 index 0000000..833e8f8 --- /dev/null +++ b/load_tester/src/main.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use async_trait::async_trait; +use clap::Parser; +use cli::LoadTesterCli; +use load_test_endpoint_params::LoadTestEndpointParams; +use log::debug; +use rand::{rngs::StdRng, Rng, SeedableRng}; +use requests::bench_get_account_info_request; +use reqwest::Client; +use rlt::{BenchSuite, IterInfo, IterReport}; +use tokio::time::Instant; + +mod cli; +mod load_test_endpoint_params; +mod requests; + +#[derive(Clone)] +pub struct LoadTester { + cli_opts: LoadTesterCli, + load_test_endpoint_params: LoadTestEndpointParams, +} + +impl LoadTester { + fn new(cli_opts: LoadTesterCli, load_test_endpoint_params: LoadTestEndpointParams) -> Self { + Self { + cli_opts, + load_test_endpoint_params, + } + } +} + +pub struct State { + client: Client, + prng: StdRng, +} + +impl State { + fn new() -> Self { + Self { + client: Client::new(), + prng: StdRng::from_entropy(), + } + } +} + +#[async_trait] +impl BenchSuite for LoadTester { + type WorkerState = State; + + async fn state(&self, _: u32) -> Result { + Ok(State::new()) + } + + async fn bench(&mut self, state: &mut Self::WorkerState, _: &IterInfo) -> Result { + let t = Instant::now(); + + let response = match &self.load_test_endpoint_params { + LoadTestEndpointParams::GetAccountInfo(pks) => { + let index = state.prng.gen::() % pks.len(); + let pk = pks[index].as_str(); + + debug!("getAccountInfo request for pk={}", pk); + bench_get_account_info_request(&state.client, &self.cli_opts.url, pk).await? + } + }; + + let status = response.status().into(); + let bytes = response.bytes().await?.len() as u64; + let duration = t.elapsed(); + Ok(IterReport { + duration, + status, + bytes, + items: 1, + }) + } +} + +#[tokio::main] +async fn main() -> Result<()> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + + let cli_opts = LoadTesterCli::parse(); + let load_test_endpoint_params = LoadTestEndpointParams::get_params_from_cli_args(&cli_opts); + rlt::cli::run( + cli_opts.bench_opts, + LoadTester::new(cli_opts, load_test_endpoint_params), + ) + .await +} diff --git a/load_tester/src/requests.rs b/load_tester/src/requests.rs new file mode 100644 index 0000000..de2106f --- /dev/null +++ b/load_tester/src/requests.rs @@ -0,0 +1,27 @@ +use anyhow::{Error, Result}; +use reqwest::{Client, Response, Url}; +use serde_json::json; + +pub async fn bench_get_account_info_request( + client: &Client, + url: &Url, + pubkey: &str, +) -> Result { + let req_body = json!({ + "jsonrpc": "2.0", + "id": 1, + "method": "getAccountInfo", + "params": [ + pubkey, + { + "encoding": "base64" + } + ] + }); + client + .post(url.clone()) + .json(&req_body) + .send() + .await + .map_err(Error::new) +} From 8bc71291e03e618fb0c42624c38cbef32b515f7c Mon Sep 17 00:00:00 2001 From: armedx86 <90354404+armedx86@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:31:12 +0100 Subject: [PATCH 2/6] Fixes after review --- Cargo.lock | 4 + load_tester/Cargo.toml | 7 +- load_tester/fixture/accounts.txt | 1000 +++++++++++++++++ .../run_load_tester_get_account_info.sh | 5 + load_tester/src/cli.rs | 10 +- load_tester/src/load_test_endpoint_params.rs | 11 +- load_tester/src/main.rs | 25 +- load_tester/src/requests.rs | 89 +- 8 files changed, 1113 insertions(+), 38 deletions(-) create mode 100644 load_tester/fixture/accounts.txt create mode 100755 load_tester/run_load_tester_get_account_info.sh diff --git a/Cargo.lock b/Cargo.lock index 685767f..c6defef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3015,6 +3015,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "base64 0.21.7", "clap 4.5.24", "env_logger 0.11.6", "log", @@ -3023,6 +3024,9 @@ dependencies = [ "rlt", "serde", "serde_json", + "solana-account-decoder", + "solana-rpc-client-api", + "spl-token-2022 5.0.2", "tokio", ] diff --git a/load_tester/Cargo.toml b/load_tester/Cargo.toml index 5bcf96e..5986d34 100644 --- a/load_tester/Cargo.toml +++ b/load_tester/Cargo.toml @@ -8,17 +8,18 @@ name = "load-tester" path = "src/main.rs" [dependencies] -# solana-rpc-client-api = { workspace = true } -# solana-rpc-client = { workspace = true } +solana-account-decoder = { workspace = true } +solana-rpc-client-api = { workspace = true } +spl-token-2022 = { workspace = true } anyhow = { workspace = true } async-trait = "^0.1" +base64 = { workspace = true } clap = { version = "^4.5.4", features = ["derive"] } env_logger = "^0.11.6" log = { workspace = true } rand = "^0.8.5" reqwest = { version = "^0.12.9", features = [ - "blocking", "brotli", "deflate", "gzip", diff --git a/load_tester/fixture/accounts.txt b/load_tester/fixture/accounts.txt new file mode 100644 index 0000000..9bd14f3 --- /dev/null +++ b/load_tester/fixture/accounts.txt @@ -0,0 +1,1000 @@ +6frhG88pwk3R16EyrpnSvQfwpVUJsHEBGqD7sUQbzM7r +CT7xarAwt1nbZ6a4WovaqZCMFk32gh4y8pfNn6PrMcsj +CeWG6SZ9qx7DYiSstGzvGFM7C5MmWChwK3eZRfZyuq8m +2qYF6zfV2mseUJG44nXu6wGX5A1D4qrU49umCxUGtzgS +2FpmXxPXqtxfTMVv45w1dA79i8H4VbtJgZ76e5oocg1W +4XentYuReHU3Eas9J8Z79FQJAUegw4j7bg3JW7LvqMwi +83ApTeE8hWsbpjzTRetcG59NURFVMqBMfDTzcTVPq9YN +99D4ynVvkqt6zm2D7RLUZv7JM2ZAHHwuXPi1kf3buo6x +CYB1veqir8rKYr5C8NrMX5AXGiPP5iaTT2UH3UiYJ4FY +2Fm1oyoeYcwGSJzn32B2PiiCuWtnZXdvFsmiggQq5NxD +2y3LZkqWBN7nifbrveSmnsmybKQsJdy1XXNtHK3acQmV +5mc4CeYzhGVBaXQK5jNTy6YYjmTU6tKRzrPm2EaddFjF +H85875eRkg2NBxPbviCqiBUFndZAk5JdjY3H6ewUCGRx +76YVfRmPdz5jQ1PuJrnznraXerC4PGkpF41ZfYCSLumi +HwgXtQaFassNmHNzPKXnBF3x4Xh1w2xDy13hFCyaiaUv +CLt5qgzuyoLHHXSDFqB8NsUk6V8L1ov3SpuNHCEezDKe +4q2N1wE3dgdL56gtFFSghNzCJZXaiA2dxFPpN6LyGR7q +3YV6wj2K9bc6s6qqQFZaBsW9AhXUjavBK4mjoUFX3Qjp +GEcfRfHm3B6e39Lod1LtzDxyJ4ZxAgtNuWaVEipvdFLN +CDPEUiCajGyPjgLEesuwyk8An4SRLAKcUgs3pwvjkqb2 +Th9wHmtV3vs4MM1ygighSGoUsj8eEoC9KfKsKN68xR8 +2QqsN4rA4Pm7voqVoe22spnT3t2z5fdpjg4sgfKdGJVf +MFHgrE9PVfesgsY1VVpy5UYXqt8xSBLxkz7WCHsE7XV +7M4sCo9MzkMvnFzt9EgLNxXiWoruz9rsnamDFTxSM6BV +4MLBwQGYy3suGjRSu9yMsNTFA6pacjA9A7cDB9fSeSU2 +2i6kN3rep7gX5RBa9NmgpMpTyXiC2YXUk6o16Lu8vipT +7mLKmZYYKE94x7KZFtQg8rV5V6A7ZkPaNcHewN1QKZuo +3NRoSMFpwdtvVeu3Y9F7SSqNAcxWCpEjJ8gYqQssFHqS +2kczSHvZNTHibfSc4WmcVweioTUizixU4wnpFeRgA4jY +4dxZtSecV88y5vM1v7mztqR74zYRUUc5Z9xm1hcEBvMt +42dRXzyRNeUQtvHjbhiTCFG73syy5fRGQCumNUEhv9vU +Fr1GNTh5mVgiiBTsmXvQktScAr67wx8dQJ246h8LNAy4 +GLyKU1ZUjfiKh3x5xZuHevPCwCS1aiZu3WCJoxon9ni1 +3bKvgEw3Nv9iSL5s8isdiJvzanHQ2GBci3wKzHGpKfW1 +AYCTGteEevxnbgT6XWUdWQXLjggomjAfKZ1yZzXwc8Q +FatxXi2fSFh66dz54fpgLfXMQENfsQuCwZCPgev8CGrM +2SCENghcCa9ioCvhDhbjHsnWpyNMDevCfQwgWHkN9fPv +Fww79Xqg4yosHghFoHTRJ9Px28S8mLogXPLdc5odYukg +HPMFhbaErGoHeGV1pffh8hPE2n7Fm6P26KuGEhpSYFXC +FdE4BMnQS9p1R8Be1D1ezJbGtEy8dbx4sLYpYgsdYxcC +2hwtUNJyo4rSEGsdoo6UZDGDe8UUhnyEcdEvoDt83aLU +3D4Qp3SairoL7za2WcVnWAUMydGj4f9QyFNabEMVEmim +2kLZiENni9HZwyqtKedqbpvMs8VKtU68jGMUKvUiB2iW +D8GwzrS86k8Pcbi2PdSgQ4zvM4VupHkT3hqervEHGNRF +6DXWL6LDBuyd4NsHfWftUwAXiaoQU8dmYQt8Mh2ExuMv +AqDWi36e7tGDn61Y5H3h585BYzpPbNoVb8iwdWPZhqnn +6huZtaWxMCs3zqp5KMyYCjCAfwSid2GSV3me2EATCdgn +j8wMZNeVvAQ3wNhpw16PwzjsfQcb2nT6gZnamtVqVZo +2SZQii5iyFW6oVhwJiMSGQrzFZxCuoJkzoCYbYBWs1ME +2HQjiXRqx1kofPQYkiYqQyMyrAKv4mikEtiNTPmXDbVh +33RqJFScc7naFYNa3dPogb87w4e9fTfVVFfjttfwgShx +2n1AwNC6Hq3rRrhDdZqvk5CAKtKcxX41DzAzeTeZfubz +8zAUHPexzCFFdQBbdSK8o9KcXv2LLZrdHDrXQnN1zDx7 +GPFBqBtKJjBvSTuM8Crad33FKUnibFKXgaE3LrXzoE6Y +kphNBz99jau34SwGykajayTsMMwd9SXdqACEMaZkALW +Ht2Kqut31qRVMKEWeumFWP1t5vbFT9hYJo9GqxN68jTF +HJv3sXY3tAn4C2NrxrkNgjXjCcik9Kb6oBYeQaMV6x22 +8R3PLvoaSnvYmUmpxrGBWHi3YJEbycJPWUATQGsJ2AqM +69ZXEjhLG8kBQhznkg6GvBoyNykHePzTq8jsb95pRGNS +5dbtAePFWgrMzsLYGUNEr2cPPvs2sp2daMk7ahn7qikt +Hr3qFDiCbSQ3s6F4N2JfzBXPUstuMb8sr3SC6DWc2Dtu +5SR5CGgA67P64PxKeB9k7C8nJ9G8VymVqYhCawq29LxP +5TU3Kr8u8bEYHpeJ1urCScWwopoomXFAEhUze82iTYFo +HWpTTbzSGt2pcsmktXtSQp2KHaHFXyVxqhGetR1UFY9E +DZcF9uLSbYwV759ysYGpzb6aSU3fcfSaszUP4Ucj5tM9 +Fbutmr8c8ZExvQms1fZVkyPmwnrC1BAekQBTEcz6mQrr +4pUq8yF5pKHjW8nUH1uyQwYBGRc5snWxsdHjV2igy9f6 +ZtoawqPBGCCBrf9SZfw8LDCWayD5wMcugxBJYvHfYrT +Du4mixV3icU6vnuGxqZy488wnJAGSebTG28fJ4ATe46N +EN13iWgr9fe1ioQyK8NFtedpGzvYsa5R1PxLajUC2xNp +nWTPEWjLVj4DP3TkcDMiNwYd6ZNqgsTwYn44JsTdaXa +J9YaExh29JEMiDfx5ZdPJzwGpWA1Q1WbLKkycEcgM8DB +FMLetdktohz5HD7uW5J1ACVdeSUTxbfGGANqhHWW739F +5CwGfaGTQAuNcSKBLtYdj9uFs5KjRkcJhfw3Zjj2vE6z +GeCox5UGVUYAykabexi2EromEiDYkhf1VrQeNNsbMFYz +3Jt9ZkzhgpptvzjmNwhMgT6uiaUQjGzmrxBgcXqEpRbX +GjqFkPwNX9mfpipM2wwUwW91KC4SauvUAKnYyXRkJKm5 +3BzYaxe2T5kD8NhCZCfffqeZnCnM1SZabhN2cr59jgeP +GWqcbj6i7jvDvR3ovVgbhsm5nNACKPiyWWz9H3xsW8Aw +GNT5zZ4PtxCA9ptqpJ4FmHXaRKRowhVeFuYqwWBzF9QK +ACMiDjvhWV4yJ5NeZc9Aqi58K7McsaS96ELADtxkn1aR +FZ57dubEXQ9AvvjRY8v1qVARFtNWm8B4q4cHoFoswqow +G5sbW1mY9L3okoDZ2tnpjev8xq4RoQKNF5dWzm6pMgCd +euGUvsnryZo7BzDTwXtgMhgmPV7h8pvZb2gi2xZWReL +3WBGmzjrVW97Po2VyFsRrxeKpLttqvbbuMRFf7duiMAL +GAJcxkZBaya76p6G2PEDyhgaPhiV5CE5BT199JAauBoh +Fhqi92xxExw6cqZ68CDU7D2hsQ53DGizaR44vfoi5Xoe +43HW5d15nkswZWVribLq1jopKTVFk8pdh4j8qMsJxcg3 +Hgpq6hwBFU63wctjLz7zWko5ntZwP7NZqzLZ7TRdhFVJ +FygQVk5DJY79D9Nx49gBrmrbXaW2qnjaonY86QcNTZvW +FrAtv1PXfKmMoLTrT8YGNMxSp1NM9DmoVRr7ucsDsXKh +3tJm5xqf7SxoXJvKXo3EC4sPPCQjRAnvTCuddwiin6yV +3TFXm5QLpT3w7mRwaM5rHjxaWVSyqyNPmAHg72oWnnuo +GGazpgPpYv8hKEtf3aMepi2tuje6ZarSeH4W4LJHwusE +2gn7uZiMoLbqLL8c9kqsqqmzFd9u5y6T3Q4fXSC58vwY +7YXL112UANLNoN28zTZq9BQE5E1GUUbfhjws42KrGewb +2LgZjisKcMUZayw64GhkHTAMaJwDdUWDGonoG7MTPyX2 +54kJCGZLGY5DDXaoyHxmwLCCzVepFURCgtn63nkMpVg3 +JDJwDiQmotP7vEr37dKSmWznhS3zP5S3jM78J1dP8Fgt +HWVTRzm3a2evyUrwPLSoTQ22L8ihnX7nS1Et8XCsNDZM +GaRBMu7K6QcyMC54H7QNDC135M3EQEeBN8G68zNuQezV +Dc4cLYFN5ia6dPbqLyEgHVYqybNVPDjAbRaM6GVp9ob3 +FbCRHvqweuG6SXdrG9Q9wjqMvLPg7byz1KHxVqzJo6Wk +5u9jfHgUWPt6AvhJBi2C8ackY9qBHJ6Y7LuQJsiQTgi1 +G1sQmQyNvrWtKZX1uwnsci7bJh1YbM4VVZ6Up2Gb7kHU +BzwZ48aXa4tc3NUXhxYch8ByKqV3QpsdsR9Zk2K33uPp +J6xN4jdSezRwpBXQerVvnHP5LyW1B43m6FWink1zScXG +3cL3ijeBqhc3SY89rDjYNVMQFbThCqQCWorca1Sbsxu1 +513BD4rDdSixupYGnc4Nwbqwb79fhPURDbbzd8YiFHEK +HYWdEYmHhi92sZVJ3ZQop8n81re2UVN71j1Xm4VTaUAa +FoYVs2p8LeECdj7kmypaaWHWnC4iQ5eEdTuEYKQkf4me +EFYjCeF7YKY1vizEzKPSWUoLLgJoP9sceLa4PYSPFgbi +HFe5rjxP1LZLEvSvMHTGE2khMYuo7hsiSrpwpKhbTygM +5RJP4k6KhVH2Fb3N4qzmNGtLjUNjp1BWNjHGRV1TEW3y +HGMugyfkoM9GkgcWRuvqaLSQSaMEdHB6kU8XppVcwraf +VTdw2ptzEDQeDjMhoxx8SPCDopTjCtuVcGT5edXxWds +4bQiscahUqT751trHFuyGxKzA3xhYiSHv2YcrNeSXcbU +wjhDmrRU9ZMkpV946CKZ6myRNJuERaSnZiFghF2kZ2r +HmRvkaAWovG3KXhzps6c9WQsgZ54W11MCoPaey88hCUt +26SbmumrpvrBcCH4nFZJurqLoLCNV7nWtKcHrBiP9U8T +284wNiW47PGCxEUMfphSKFRHHNRcQZgBfQSW2oijwGJN +FduPMXyWhYZSN8cYMRaxcxs6RJLQrLTCxqckyioMvd6v +HfeHnMGxMMHoQneuh6q7RxMhWD8MCPng2ycFnagT5bSS +2XmBuwtgVrdhYMh9oibimXSjkFb98Yfqr766JDRsiUcV +2Vcd32rN8HbiQr83rDp8fCXgfNz8zTJn3ZSSNRcc2rao +AHF5xJKoPphenopF1iqPC5jKu82obJZUPC6WKsLjLZJg +G2NgvTkL766waTLJcwwz9e7GQKX4skMf5bbTTcDRr6Yn +55K51B53f47nuM3VLH6rc7SVCbenDjZ53fYA25wQCZNt +EHyYnrJW9CN3MJZZhu7mR7wAEXxqF5Yb4UFSEJQWfQh +UrAdk3pB1TCe2aP3ZKuwsVonCAug1yQfNJz4VVHsGwD +4Y1EnpVYTmCW7qTvMBaSksvcFNzQNdHoGnzjuipGB1Ts +GnzCaVfmdj8V7AyGp2XERVG9nTGaBfjmNm4FCyt9Zbsd +GSejEdiUNrY7AF9Bj3P7Bh9tJgd5ovPk9YfF7GDJRgWE +4aa4wgJxNDgeFQ4zZrKqBPx35dCk8dk8xezpBBEsZbMx +B3jFvARvNaLpsWn3qEKCYb7UCjQgfCLEz1L4kQMoRxY +3pBBuhC4MW7gUBGfuM4yjoAxn5RgrvrTaG4VpbR7vxuQ +JDNvv1PJT4s1fGjcT8kLXqmdadyqwr4AbkHi83jHAVuS +7SbGaaLN3TTHUNCFdZcb3DyCEVFdxZU3NWAZPvEgxWW1 +BFkKop8cxULRkcBRFRK1wM38nESSAPDNqYQHEbY3rSb +3PGAG1QdLrRaQEEHjMzoMYsug3GjsjGiCeKLFPQGJnPT +HLMVn985R8q1iTsKHkhtP7ATykdgS6Mwy7BPMCCsRFDA +FSJeYuwpEwDjECc3rLvNMXC9EgpjEMebMH9tcG984kCD +7KDoref7tAe5pnABiny6D4wStRYCwobdeW84UxRsWC2w +GHRTz1gg3gTmCCKzhGwn23LdM3bA1ui4KGBrMdxVL7qi +G28JbQAyuXVBdggXkMHNtaTQwjdfwmzeVrV8KVw3u7R5 +4KbJb2x3opLnLqGBPWmq3EvT9BYEUfqKBHVr6SXa5onT +HbxHhcDCNhjfWs3ULKKhokR531canDRKAU2xEMigzgTN +2ALhfeckzRcJQnJfC3VzH6d69y6Xq1kMAv1p8pMBP68y +H1yhE32V2dwWxQ7PjNLgQ1272Ws4V1oMN3SrfcDVoswa +DDSBgNDLopKMDW9ApeUA8TjozYjwmDgdSTFoEM3kW6K8 +GX1mAU3QGcd8pqHU1AJLJpAGnuLSSSgW1KkYq6MPm8gX +DfpR65er22QdMSBT9PMjUNm2wE1NJKdG7GmupG9xFND9 +FxFG3W2V6XsRr9zNvU6h6EgYtrP6qDL7HSEgUiJD4VqQ +2H8LXHw8agpVoCdgXRosUCRhLzBjL7MeodXGyiVxadKK +GE2TFdaGfZFRzedwYXohdfaJg7D7HF2tJUtY3sJSDLTj +H4h1qqkb6yk1HERuVFV7h77Zp5oqKGZanZ8PdjPZwnid +aVJZd3BkUf2Pv8d6xmwme5jtiCHKuARXdEjnBX56zoy +GVvUihkSf4mvahUmdeZF3RoEoLhG7YppJy2DsWXt6kAH +H2HkTsLLK5FEdKENoJAtASTphVRFPvmeBv93yBsbUgwu +D6xCUcbhhhMUFBp5opPLy2gV5WgvDZBApdtgXErMGDLZ +GiLLQunQERpnZjfVQbbbFjHvfo4icWyWNegPrV5dHgNE +H6KncQuyVoZMbW6fsufKB5ns4e6QQsEMACk6bpcmmApf +8pkDYZeN3W9bRMk71gdCuAbV2JbUbUhBNN2itb7swN3q +H8AQSc5NzJEMwQzUPvbaCVYrr4xFq5Wc78RMJhMVdoYE +F5NSB4PPdkQRsyHDHDSVEygeZd3yfM8jRdrdECYA6uZ5 +4bawoDDCSoM8Ao6mZGhHsXbQQmnAEKDbe97KostbtrP8 +HbT1XwUidzJuMR8JnMpgqkv2u3btdMugA6moyDwahdrQ +2UoYwCbHrZqXp4odyMRngRPERkComrzCSAsvY2WJwEEz +4swcEhizbYeLrVWrNfCCntNEaugHAX7muv51ziNczuQC +FVzFXqN5nziTbur1giwi4TzYrUmr4hPy2RfxwbCLCLLe +KBbLym5gtBZiZ35HX6XECE4gCvciy8THtDz9skrqKNZ +J31bnQco4qZH736wx1KUgXqtKbCWw8wxgdoDk7aZjkpu +HP6Kb8QbV8ec3v7BLnVkQjbEEBtygSXf3hRzp7KwA83h +9NtdEcLnTUGjB9Mp6qEbgKmMwUocFigdV6CdBuNHagcr +GigcEeF4nkwkmpqa3rQwxqz8d3zsBLQBDQhLVkTHcXWd +H8kKJA5Qy1ggyjzAwbLNvAPTtLNXjssQ8q2m1FjKgkwG +FAtEbwctm57QNo2ocSXC7STAzXyBtJdnc4e6LCQENZM2 +GyaPkzFo8WdLdS69VgggGHErFBVZ8EsEFB6Br2VuKB7j +4xtcnuMKuFojrFNwp3Lp5Vuh76UdAv2vgjBxK3TFRcQB +F3VicnnYQdWwQpK61UCnmFMyzxeBMzvi2LUc7vNEzjF8 +EchgtTUSWG6pNB3U9LjBenXHo4gkSAs7uAGfbJsZBMnK +H5o73nLDhDv33yPcWHijm8rwLAwaRUZVy4Rp4Ei7G6WV +FgbCSZsvz6EPwRmNL5gzx6pbywgqa3BpZ4DFWba1QyDj +79Sjk1sN7trYumxoZbJUynoHiFGLHBahvMQDqR3JLNh2 +3WhqGQVhLwqRwBaUjXGE5oP86WqSpXPUaRpN7exBxM3v +9cd3edjXe3MmgJQoYqBdus2qWwnmB5bpwZVkbBzsLVD +5HVxfVLbiSYVxTkMzqLTgrK7UeTMPt9HFFPsGUs2Pegu +Gxcz4Bh1Z2v7wMHpr6rEstUY48rcLCnAuMBmeACJrbdV +fsDddqMVMmkH7M3FgZPiCgDJmHfmQJ9zEB9L9ZgZhuB +FbseWtm2iLVJ76at9ErZofgMGDYyDdkwZMRb5iwRewc7 +3dnfeg4zornR4wzmw65vnx7zNhUiyA17nDjkGuvVJkrn +5vKQWJtX7kvvszRww818eRAUxjd9s7Ecgy3X6LKQVgxe +BA44Utb8WfsTyUeqMXU5v631k8ULMtPCcygoKPnC9Bf +qDzKs7RxUJQYJxWozB1FwzVmcZgWMPpBrfSm9xhseLP +HseTLJLvPvY9HMvbGyPSxaiHGd9GtRWrMt56dz5HNjhi +4TjnE1grL6WHfbvGsiH6o44QuM9tZmVubZd7ruZu4QpU +F3WWBcpQFPUFbFscnpcmSUv7MFZfsiQjxnBzAxJf7bHS +EMHqtJHZUo6s8VnMC4uPG7ESDqCa1Xs4PqzGhaS7tKir +86YxNr1bfxg4C6cT3eCUkipiK3BD4kapvcc9YFU9hfMd +5TmWbmKBbErWA1bZmYgCkbtxJwb5JHZX1CE6KCbLRrPt +4oQ4EjBhZ6F8uYni3LBkvZr72MBQwFRnHsCf2PPdEw1d +6ngVhf7dWtvJL3GE7i19imcQib8detwYhaQnFvQrJuvg +6WSNh3GHo579w2aCmfNEypvqon1q69e9aYUXVUykx59K +76JQ1RSWbb1TVDmXwJaU6YrBpUP4xnsnmbBr9FPUnCbQ +2Wv2eM9MzDzPcF7G5LM6kGoJwHLyu2hm2tbYY7sQrr4j +9qnzzqAqjeueZ4MkTQYopemyiJYHn3Erx9HjruRPyX5W +HYidpxzrXAdusy6jM1r9Jp3VYjrnFAcoNRyM5RWk1R62 +G6DFB6oDMcKkPPN14zjTuvYf87KsngyuNNNyTXRPENby +GVhsxpNux7P5gMnSkF6T9TdaLWTf9k76GXxqGMf1nPid +5rsdPJfJrS9w84Ka4BBXXcP1uQwrLQJZG26owFNLpykK +DjNjHHgsFUVjoaS37qfyYoaFMAaAbdjpwBwhamo2jdi1 +8yjb7BpPkLmkKaByaJeaZX8pGi2Ykn7bdtN4vwsHhSKR +7KKiuiCWg9ckZz2EeSmhVwF3nG873qcvQxGUAzLB5wA +GPnhwGPjmtFD1GwuhisDSrG9gMu14RfHKXyv68Xyndc2 +3ZaBy4oD4MFS2fF4h1zvd7nzbgPq9MGjm67opW9kWLys +2wYDNE5yEXrTeXLZ2iufcNZNRpShMUgYoUkVoXfZQJdU +4Uk4ChXgEPeHKL2WA7bF7ENGayazbAdSAAU52PKWSXdZ +FmbfquBbKwkBFHL3uXU4ZHyNsopbCHyau9McYhnHZ31g +Amu41aTcuxtMk5hYcoUnVvgihzq5wK7bUh7WCeExpJXe +2QrjfFgFmvjaqqGnaii5dsDfiZC11T7TfTXfgV6iF6fi +3L4n11X3N8cHNd3XqPxFj2jN639KwKTEd1RuxHxdb27u +4LhG2psb6pnfk4czMUQepnMZ1969jYGjmmrtMskDCS8k +GaEsTL8fed3a6Fq1v4KShNruTBNuDezPG7SPE248qg9G +Gs3pTk98f9eS9P1JhZ7UivMmf194dhQu6bN7gS5Hi6BA +A3AHCmCKYM8xUUyGf3vabUTKTuEfdCznpAv9wFaw4K1t +28wAMXRkuNHbb5SwZ13TRKuiwJuJ9Cf84jWknykVoYw6 +26abu8PApsb419o8BJTYKJUJ8ny9mJBbFZzBF9gVewgb +3eLF7gf1si7uW1ANiMWLQ3okN9PpdCok3igvEuCjesVx +3crL4w7FFVyzskhhvKffYLntHznJvyykCK3syRZwaKkm +D4KHqnQx1fNaMVM6P4U7Vz3wzDUjpXzXQhqU3er7JfNt +GW57px68Di2x29B2rV7BCvJBwWzAgXB5vfN1hGNHALF4 +3FHmrnxj4wrEJN8FHDqgDoZrgRzWFotuUKTHKZSQu5sK +EVHEYh6K73A8ntEzf7PhLLDDUhyChoLu94gtKG1AYq4n +24yrsnJBmaWKLwxMEjrHaM2Hm4tBqdDYvYpaRXguHisg +FxkT9xrKWe4ZHFoWE8NmppzwcdQ4eXQpXCjMeJdzfNHH +GpgpfChjTYgCpMkuJ2GgCbr2Cm69AJn9pLmdkrgdQB7u +37hr8VVAfjE2u8LSTkFQLHT8cumwQgokASxmyB4Fp1fK +BmRpSSvgh7rfDDpfit22PFj5D6ZY3DqpTFu2AH5ZcYoZ +3Pw3tC7EryQWqg5GKiEuzrGb5K8APzmfnFvUSmwUzmb3 +CYpZ8jcxCPvwG9VW4e1RztP2rHzV16yz9o6HTL2Vciiw +2DQpNtgCBxxEkXLZ3GDGTSTneVZCDCJWMs5QcrX4bNnN +CVCybHkDvZ6t7RpVA17jC27QVfkY79fAC2RtVaHooqCG +6LyAQFZiZNMoT52csfMk7r2g4ZDJa2MMUVAqedvNyq9x +GuWtniwFRupADyqT3EmqSj6XYYUdWLUWjVHg7GsQUEmH +GYqgKmp8US28BDop2Enkwai1kUtqtwN7GaAV1NsdW4G1 +wWkeHM2yWQ4JCLm6WHSq6E1s2R8xdiwKvkJ9HYLHopw +CRRie2TjCA4oxyNZxhZZF4sQLAQUCGJ4tYh3CwwQmeBJ +5ZZhrak7Uc36TQsF9uhNa8aLtLogQwqV1UD7pHRWv7fZ +4eJWR6eMYbHz3MuWruxFD3HaPAFruy6ubcyACQwsuDb2 +GJ3K2rv1bnFBdDRxVG78DEHJ43v7GdzxJnWz9RRzE2JF +CA4U6WAuj5k1Y6YVK3uHmdS9Lqpvb7P1jXJsToNAs4NS +8iHvHMATk3ksJpgdCqM3MsZL2PWWYiDbnFXpsYeVAvis +34D2KuwehmX3t8AAkfETXG58ST2MzPV5niuRmGnyBNER +ZyeP7kfAbp8YDLsEX12Qrm3sHq4xaDr38HoAKkNG4xP +6ZZ6odHoyEokXtfddrmp7HmHWC2dKy5TXQ6rK7c4zUAL +8seXQC51AcbRYVfBqxkdkV7zBzJpoYb8sxww6HnBvo3c +7X1j5oxAxvi8zRBhgjYHJJXTJiTXzMZZgkgj4pf1efb +EVU4dVG4YR7SpwGy6E5uzUN5o4AVe2QFBuKBWEcDgQPF +G7QMBoJkzLVdBmZKAqybdyDG4fvVjhFvM1GYgQ5CoNco +6xCV9hjpAwxNUaeaRxMKmUyPMHoKTWkWoUb7citbLfJ +GT7n7V3iKb6j3nVjNZbgYGYDBrsWmejhbnLFj1JjniJR +FdSk5ZWiQfonrWhb383EZTPEh4RMvKkWsn6ik21vxHCz +4bCY4YJTT8UkARygTujJJ4G9sNHWdjQZjsGMU7g3bC4S +HL4DD1oKR5Eq8jRCyx45amHSXjJN4E5V2DN5fn5yzSsw +3A266BtHBegYpJ6tyYERGxhxcvmPRnGPzgAPunCg3aSd +AKP3ZxEmJkuAf1WrUDJYNY1S9RPw5NUhWfu5jHxGrrJ6 +2EPfLqDKchLvpZBUyZR6P6vapYYPy4uztsFP72M7n8hL +GwJXMdPqRNYPz3WgQuL3ekof9WXD2ZVWydq5ihbT5p3Z +2gNXJsiQkVFWJP5hs25TBjdCTqpTccTNe2edTeSQGNoy +3HdGaqjDNrFwDWTRkbg1V6HmRvkKwJZoEsN6Dqrjwhjd +HAobV76ME9ftRqc8tdm9Euf7xnVjFk6w1c93eF9G2Jbo +EGRuW9Dq9SZNiryWs4a7VykckWwffCgq7VTWxMZ6QZp8 +GeZzHDe2o1UTQFd3EQgpEXSfbJJ7LnWc3W5BjHyBSK3f +5zU7b9zySnPtKBDgAYzzSWneiwD8PfdWG1hEzr8pgXX +GFpgF9fTggwVxHwMA3gPZg4rSpVpTEcNH1Ve9uC3pxYE +3okb6X7isq5xDVmV4Rj4gXVLR2GsZcMWAzPDUNWcBQSY +2zsUJQcmRgsv3XxLMd2cm96or7MFUr6egwARMLt9oAai +GPcmq7FPEXJrkQGQ1eyWoBrZi4LSr9ArZUf6b46jmzH9 +D96jRfja2kkYmayRuRA31qbKM4nxF4taLEnX5rkCtBpo +4tqnieucBRw8o8Bvk4P5UAPtivBx1cpeUy3Pj5jchbFw +Gy49HDRbevGrVTqVyXN8AVPdFsneaAUedyHRMc8Qp1K3 +52fcvjfFChVwGSMFbGYt14QMYtJxLW77y3GZGSpQjEzv +qjhWaBD7GBPNiutprmY88zufBQKuiGaUSDNc3Lvu7Lc +CqZZccDfV7GHVwa9waFjM76K1i8qwnSpyhjugcnTxwxh +56D95zUfj1QqwDiGz2B9sQyawChgf9CJYpW48BAP2CPN +EqqhXazHC56pvcB3xnbWhVyyxQ5qNeSvXGLZAm9Dk1ZW +AkTAchDVyCa3VPRQzQUNVqjQhPsdBTBuUtx8axAeie2F +33EqyKu4kxrvMR12KTiSNKN7UhgYWVaFEvv1eW2zHock +8EA1znbGZPPKEKQ4criBFWjBVw86qf44ay273ueGYp7o +5RLEiGNd3K2RRopbFnnGckUd3ayreiEFdtfxnAUvyY9B +3LC47DjmTdyCxJU9cdQ5bm78Ykbta2gs8LgtBF1fhrYQ +MtvDk3EhbQ2dgK4uZ4NzJaVqyc7KL53HcQ3UbBL8LSs +Hk46C9ZhXfJR38oVg5RYX76zKg2ngEJoJJgB9ea8df48 +4bd2G34uyw7WbhrYAu3kCBdJyb5VMa9WaHJHprGZoW6q +HVcve3jdPHYc2MJNFZRGnEg31rAqBodLqBWoiSpSfCud +5af436mEpz3ikjNkkEmRV8gbkmebvJXA4niShJi5A7YF +HKYiQb4pMNTkVPUmaQUCUyGeEn4WfhiRdfrJRwTNMiov +8uK5uLHCxdhpipNkMt7BvsPBxAghgCDgoU2n2MqMXVE +HW6t3eiMUyRM3X6cT7bbBLfPoFUfJiTjBoDvZS63DaM6 +EkLjusL8XeDkH53d6u6ygvnKgrT8vBDLPVhVFmYn1xbD +J73jr2s8BcjnbBudbNquYyEksEF7JQzpzbMkrHDZp4pW +5NEepmNn8cBo5sowjpiguGFT5tAFfDte9Q5U5qcYZwL3 +4iMz68SuviGwNT7gJXJz8YmYAeNRCi4FA2tmac1izwRi +8YdCeTzJZG1BwsTNDtBJqoNpR4yQSJ3MCQHFwQJebp77 +G72SqRC6B1PVhPCk2uRbLoV7eKSU8sK3nTNBxcqJofR +GewJ7iUGsaVxxVQ8YMJWZjkM6Ey3x9TRbft5isy2n5id +2ePVXAzdu8kmxLoqj1bzyBToE8UuKoYg3UGhKKMf5NNG +w2oH3ZNSPnFtHwVw1YSuCTyKbua11sZE8DeZSiV6cuU +DK4j5zeBWp24SRjHX4bzRjkDDgFLwhTicvWuLua7a3di +Eif6x3XE5NBpazN788SMgKfq8d8vS5TjZMBg9vE2pgr +4nseSGfHhDohzU1fqztppz5XV27aqDaAbGxdbq4WmEHR +GNfxVAGxJRfMFd7v2XnK2tv35WiZXjW3UwtW9S8EReBT +Ffxt2bfGxMXk2Mbkr2XxELsSdH3LJmGz1vxAFw72zykQ +FsTDAThg7RuH5f2Hkggm5MnhHntpTEaYLkebP9nq3eoN +3znWU7qgBsKKH6HeZn6U5UuhdmSe9FEAoQGrCnmjoEBo +GUkLs14sQ5H9KWpRsQiiEVUQSLWrvSTzhFuuxeAipVCD +DmdWrQLw41A6GiGBqScbYphBUbEdWqZng46Rza53DWFx +53biKiPgTH7aLTZ4NC1Vd8HUkYU59FrZ4WQY2Vcra76V +HVtEN5y4kFxkHXdbVYFkhyuHjKEv4xpMt8ewGir1d6bQ +HEHAQ1FESsN7iZmrrjG11TyAJra45NfKdEmh2LtTJtiY +Gk9FPmFYnq72sAvMkyRSMqJKVhNPxC3VgQK1sHXMkE9g +2qNCBDpiHRQNwcEptT8tFppSbwmUZ8V6pNwP1qirWqNR +VpexcXA2asiFMKWA1omAbjspVpuq77S46hA9tiF38hz +4nBoJTAJeEsbAZBEWctVa18q6Rb8xZz735NiNznsKC4p +Gmvtb8CyMF5FQdYN6JeR54MpqJctRT8BY9qQ7TpP9wfF +9q9HMPfSxNofT2XJ3YKJj2s7my6PhiqCjiHNrifpoVzV +N7fwG3pdPQcqVM4QWSdvk5R9ekZWE6kR9ZMrihnAKZy +H45Ji5HTtaoh3ZdHAcyNwj69L2vb75orEF4fiJRo845m +HGN5QJWoS16PB91S8EWSJx74Kan7QUc3nfNrG94amcka +GkqCVWKxmT8BybWBe1sEKEgGf2DVwXuFomU6Psy1tvCG +5bmxKd5HinP5sMVGHJdiJdjt1X7FA3Y2848hJ5ZgkTQR +EsK4m8Yk6k9KHxruuHxXa51EXcQej8hmbHeBKshGtdZu +5Gm4WezyG9LbrGpa5hA3be5hkW6R9U6nwNyNYiQrb23k +5yi2jKJ9zub5vTxeChf7hQQ1bu4UUQX9YrjAGCYzoQBr +4W4L76Up94L1wQZSNW3qa8RPtSTLfEiKvubdkj8oaqx4 +EG3sM4ydZGQFQ6qZoa69aMCwvbfH9SATprG6owqhe5cd +FoPoxVPqcjngYM6mgUSvBUvWCi1Cs1YRbVnnWoVyfCSA +5Wo4v9iMr8fAhgu7ewxN2uc61yaZwQGBtDxSJnwJucZU +2ti52PV9UiJTeY6P1fdRDxLW5qBrZ1R2Ubns6kXbi83M +FpJ4BjyeaqPkjpqRAkTpnWQpW6yURgLpfhAYvRP25GZZ +3agoS8weP62yHpy1ktAWqZZPjbz5LWQWNekQtf4QDGgJ +Fxd2jSzcLY54euLzcsfD6aYYhjYqkHBBjgAVvcHy9P6D +2c7uaVizvMjSvQPEtxqcYPnZaZBKmG2yC5Eh5Y8j1qWc +HiPCn2MrRmX2AHcCo2yQSA4DLz7eqnckgFD8p9ZtnzaP +GVBNVRRFjvsZZyoC2hAeNEXDPyMTsoffk8cYYTdEgkQJ +8sYZmAcEzywpDrkUBy1SbSN1z8mBp243FJ4r8EgS2cKd +EdWFcU9t2iiVpMpJK9yLA7CiZ9f2CLJnkkUjFucMoXA9 +xxX73obWeNEfv3wuzjuevW2ty35mPekdDBpT7XAExuG +2h3cH7jKLWu1uW9pksaiPw2wJ51159VJ42Y73jZZpBBc +HUhSWFwoMZE4ss6pB5MU5SY153JLBjDdLhmQ82eGuJmf +5EZtu7dtfWcZqH2R8Vyt1Cqte3kp8vtifcbMBHxKZqcf +uFJmUafkAMYjwBDpS2zyQWETSGW8YiJZiEz8CGKh3Vd +J57FdaE46rbGRQp6GPr2BuPxWijStJevVBpRccUEANUP +EKDaPjbspvP21rbWPavkCRQJV51So9FBWmM3NXGtP1KU +4mPgWMULTgNiMwbqSpycnmhh8ExYZZbZNMYZgTGaVgeR +4ASYCeBFCDbKQAipUo1XRnBkiVT8x2U3Ek8ziFAt9wwV +wkRjrnaMZBhwbRCFW14ATHGLLoa7dTbkBSvggJRBivH +2qzcY3fMwcpuu3bjeBLKvfiwNWQhJ74CWbSfCx5Bqa7n +3YaGubzYSfW2R6nkBbdZ8aHuYLErNPdZbxqJGSHdwWis +4uEupwQ389ip8uLtaACuKNbbzMwa43DBkkzsCYyczDob +8eQaiaFqMnpDBK3A3SnXSn7AJmxvirn4sgBZR1QiTvLo +6XRzoJdQqp9bLkoqFWXSDEWyvxVnLjGbzsts67aCmZn9 +Bj1hNMPnhQfiiH9aAV9xNPeiPtHFcwg73AFzvgExzAHC +FQNXjta8RULr2E9BxQLrF2p4rRjnccqKp7xgupeSrgnY +A2o1ytAvbNYQExbL2ZKZrUXkL1L2vZBM8Nip5q2ob4hg +JBNpqu7SPfw3abHFbMxceeKB7s4VtVYDfEB8DhLBajkH +83UPeYv8bAiD2ETd7zCwp9v6NZj2WATifjer9cNhwwiY +G119vzZUWfpRQHag7dBxLJpLk5cuU1fFJyatGtpdf6Ca +H87Yd1WZYPkHvpUi2LLez2ccfCWJE4ad2YHS2UcLyhzU +rASjUwJhL2sFMX8VtEcpPTq1Jg7MJHbSZWhFeRmbU74 +E4xfdB5MWYJMUrzDzuwALaruXbu8MBQKNEkbnXccsdY6 +HNC3LhdoewzGHhPo5smJgxvsJq3bKMWKRFFT5XNTqAym +21Kn5BgNdq7UxkQyguvNxv4nXoGoYoFLbtHvzSncr1yh +3qfSqZXrMAtgrvEP9fWM6PiFPkbVVzK14EobbFS6wj9m +2gJ7HCCi84nU4oWYxkEEryeFxx3Y3Fys1TGZr8ePt6am +DqGz8vgprNeUnoBQexuWM8rsbY5Gbm6Zw7KEjFyk4gh +9mUNLfJwCeg6a6XFYMPMJ55AhZyHBM5TRDmG2vwyV8LR +AT1JJPaAR2KPveL5Rpt93EdwEwFrfnQTs7AcG4VTbad +BnABpcRHg7ZuPis9KoGfsoZJ3J5JPE8Bpb8aQYVoPEBf +HVge9Ti8k5nEETRQSyEx5qVQC2dXVv2PQApCUXn93LHg +5SRhu1KEZcXUHvaiX3ngf953YkySAFpdfrEXv696v1uz +Fpth2CW9iKjjHoAtYsv25WWLd68WEMEaasG7TRBNTNyd +21w1FX3ohkP5BuKWHneJ9LcwBSrVgcZFX7QPvnhzp2vU +FTCdghPggQRqab9mjE57dzeK281FHwssz7MFNJi3GPY9 +GTX6Sa8B3ae3kK3v3X1X24ZMusDKZ8qfQuNUne5Qs7zS +3hwWiqB4ChaScNfVdnD918th9ZBfr6oZtiMNhSxcriou +76hCiwhDp7n8BBSsBoEpWZJsyTykcUn9PJeqV3Sw5RNY +H5S8pQS9D6D3fzwh1GnUewvRbm4kaK2sKtnL8na3J7d +3VQKvcTq6gJCzJquNerqqFujtnzAmULjCRhoDuH6boEu +H8abyLeBhGLPoq6ijXjtHYdj96w93qNcBPGmKzBURHbh +4jPktdpq5zhpUWaGZ8FqRpbfarSxruymphX7VWJVHqZv +AV564WM755vcW1GVyb7UQ4E6HTTdATDU3piGtMK1r7AY +2WB75fn8Zv1XyUBPYM23qmLKY9GxzfvGrPEnmgwPwqLF +HWfyZ9nE3nbMeqDhcrSqdfkWsQ5Tfq4cG9TRwojADziD +GtjBuNooBNw2BAveELhvD678gUzhPVXTQYp4jQf9UnGY +49zEsv5ReLwRGkhqUhZjwSgSszqguy61itDZmKqmjbaM +HBamFxYgUNjHzxJ8Q2bG9Dz2mHiq9fD7S1YYf8E4zMyS +HFwsu2id4nhBr73bkrRvESxfrJzdmiZqrqhE8kdi6YLe +3NvEJh5VHCFYKRNLHbRaXmZ7f2jCmiBkRnH2kUysUKjJ +CAuLFj5vFARmkRCK7uV1r9ohSZE9eD8XRQwrnGxAzvT6 +9Fq5jg5KdwGisduqBynStjQSuX3i7cBTadnb9Hj44JfD +9f3zV7V2a9PYEkGb7GECYLprvhTL2zG3zefttsCuSrjc +Fasp25XKCG4pnXZh9hXtDw4MpmgdjzPwob2LN8DJEiKh +JDkADqHjXJG5hqmSAGvRHrxq1izAkawrXFcYY6FCXTwS +3PhJVwKcLcyqiAiGMJkRqAyj4BJcRBUJxSJ4wG2sCbTc +3kkGjcQMqCgMntVgAvMQRi9SFG74oYSTaGWF92xcRHxu +1SWqyzhXz8P1VkzgaXbKipaA7fPpRFTjowGXxgJz3XT +Dmpat7JoXxfqzaedk1q9L35yGTSbjLAPKh7aL5QciEdJ +HbCeSqLpitxNBtrVzZBEczEHyv8FPdMa82nXRNjU6K4e +Ase2DhHZLXTUo2JwJnbPBdNsNqKfAet2SUG78sPQG2m5 +2C5N8gjhYyydyrAncaUphMTzSwJQ9Jtbz4nbFwMdLn7m +HncQNpSPaK19QmkCgZjLhiSNY4LVdNm9aiZ3uvq3gsZr +FoXCsRZAucUWJDSfCo2zxUQtDkGRmfUQv5zcg9HgSBda +3sehsnJj254EtdpbtsHEDmbG1bnBSkXXgLery8Wk4Mnr +6WvgDkC2BA9NpMxG3AZ6FQ9rKHxqqvKvDqEK8G13XRMz +JDNB5mqEZYdcmeqUUdxLMusWga6r3uX6HvEeu6ZD6jwv +Huq4nMEwtf2mCFQF8qc54eYEdaVPMcQtx1vo32Kw7jrk +5TWifmjpkf3HoDZeckgyzfEgMMzzkLhWui6xU9QjfhUM +H1r2N8V5hoJTMrw2eSYHC3N2eQwZdsJ34io1jm7o9QWj +2i6fPThVyM9D3NFizQKUBeAf4tCsZS2CRwdS2ngdrYYt +uNCj2uNS1Hx2oY8phFyCecoT3yJjerWgA13eFTquNw3 +GcSLz28RUzwrd9tuS85GAhCMYqoUYEmhhSbfXyKWvRnD +Ex3GFBFbUxaseocBevCUH4c8NCzJi3c1cUNUBMvX72So +3mX8TpamkDs4UxbctmoYbSANkNnax5vkdQYykYCFrrhi +3WwwEYVMiGyKM5HRRBa1FUdCjWEsdcUY4UaAfC4kiNhX +Gug5w3cWvAfrLNnyfkSGN54G9umWPu2BpC3eH9efCbUA +GrusP64kTuBcKHceg4Ku9M61KCMDQddvf7DHDoofQMAi +H3K7FspPnSxTVR4pDjh2EyyXRgXi25v2ibVhKcz23RQu +J9vvvxzDjJe4qacu3EGZkzMVZ3XDcqm5EQ9ePnUafPeV +2w9PwjpGFrY4kC24nTqvgZPRttSu7uMsDZ35giPTJNK3 +7xxC12Ky5bhPuXwfxGpocRgQttRkkqeSrYNzVQMVhGJt +2ZiyTPjRoUyXQ3JFGf2ujY5SUmb2xcgXLV6QTV2p57mx +5DfNuCxf5stybCuwRB5BjE5o4Zkzn2Yi3xZTQKj9KHAS +HdTua5b3mRQB7HnymCBf3B1Ryh9PnbenFh4wCHuZBs48 +EBRoN35jpKtLL94LVvi7KbuS17md48nzLByKz85YxnoB +14ZYTojvim6P6Sje9XDmLzuVkddjhMnJhH9yEuueMCtF +ENzkWtYxHoG6qzN9oLauaFno91gx3PYRntEP8YEgFJUh +2svMJdR8rYGjLZHK267SLeeebgbogbsjvBvTNGtAAjE7 +6Ej7YSuRJpRHbcPouNukFzHMBboWr2DUHDpEX4BQBUxx +G6MXdj1gEzfChKM6wfZR8xJQYSBDFRCoJ5JXkPUndske +GuNh4ADGTvcdAeiDEjxgLuKWjptyQiK4Qbcx6ZDbKa2n +5Ltg66xBxKFaWbKZD3MvANMEkQ6vXuT84foGDekdhGKn +2sNUJi2Z7JLyNiKtEm3HGArxCeHWzLPQo3XdXwoFQUPX +G5Kh7BVuFPvrMsY6qn7cdUGYrCafUPET7pgSHXLRKKWv +J9EmqaLip3HLcZReAv8fikxWW6vRsbJVmx659Mxa6e37 +Ns4m8DQbx6K1NncEHZRw4ZppRJVazS9LQ3j3F6XfLvZ +4kxbvCyFLyzfydkGeEXYSurS7pxUv5wPg5iMUGRcZBvT +HCXFH8BqGBY6cTXKXEFCrxzBRjK7bhh31cLmMxpLFqMd +423e3GiXV9mL4C5AefZ2BwgvPstWxJ22waKDXvMjPK25 +N27K3Jix3MVPpMixWa1CX5ZR6TRZnegwtozVYWRcqku +2CAMdCtEvEA1QGnNST5y12cAkvXKFyJxszdsUy111YAf +3J1CNMPPAgPPPKiX7AAmHkAej5uWEBvUCFq4V9sEoWWh +4X1GXz8qYckwCbFHT6yA5ozct7fofG7vmK3d3VZ1EWSu +JDRYLJSyV54dZf3s5iCxc3yZ4mSadgd8He4WD3cgRQyt +6JryseWyDwJEmW8P9ZAr3Jfto7icwM5JBQniZefRvm7R +GknRRcxga4ZSHZjkJWJWV24oc8g7BBadSytjj6DHS1us +6GiRH53nTNUPunQEFN6MTUtY3DknxZHigP77RKhee2qb +HygfFZLSZSHPPb5iVBCNg9fFGFhY3vTMPhHmCwL4XLdf +JHgsLD3CxWnxfxVvuJASx9VAdJgW4fDFYa7umRqpym7 +64pAMXW7AvpyZGjJ73eg6DLHXWNfjp3V72c2k7zjy7cu +KX5HcvwBz7LG7EmvoSmXj1g4VHgAcgnjwjSnEmZCbR4 +GF6YvfANuCtWdoen7NNgLRFYTAJQ8KvdyjWWTF7DKADb +FYDudakuggFRhDCznTqyxVT8geFckhm7kSqsYXLrgZGU +DkWq7ztMVvf2HaRAZH8FPDv8r7Q8ZT9eYggAZMok9qNe +GSUybwMi3ii6ME86YbXKDcDXY8jyJa59rWKuLjox5Loi +GikQD9sRdpqM1jPAa5p7xbemyTkNMKBCcVBPVere4gfF +3UJPoNoRLWqyQDD7HgLhdKGDqgYywEVXNB7FRn1JEQsd +23E6D1p64s1qHEHSFYex3Pb1wx7n1QLqJsbHwKrp3XHA +Fa4inMAUt4ZNSXNeyvdzU61K19K6dx7PEHnQ6d4hoXWo +EsxzGwwkHZ1qnE9FbBDKDMm8fbj5PogiZHva14rP6f4o +8UoVKbZiFRKzAvwGrgHWcf7sczzifoEio5SKABtgB5Fn +A8Tu17zZh4cFrRDXMWLKxiFWj5tc8o4oDkX5YTCT2ew +9QKmRv8RScThPuk3Jr7capsMXPunjBmQF9GTdaqaWXTM +2mmxZUij9sAwmfDQgVA9zohcpZnjcR4x4DA765tU7myx +Fy9pQNRPAnURwzVT84jQ9oduavYBgRcPdyRc9YHYpnpM +GZRLP284y1DCCzvFyqCJFBhTXJkErsZv6NHQ97c9f757 +YHZZwtqKaSDebdathaZ7vkwkx94ZXPmpptV5HjhFKXQ +A9pkb6xyJbGycGYYBiYdMiTQZRHZ1H9mxjCdtXkrv5pY +44XTEg889HMneiJT4wmdcZ4jBTZ1HW4KTnd3gtcp7aZv +4gV8aXrMXpbhna4rHUdqdgSRVqaD5EMqU7SqhnTop8L4 +GNTD6bCtSQ5akiHue24A1RBsKFBTE1tbZyKbDkuBPVqk +Gm5UZYwmJv4FyZCgheQoYYqj1hQDRfc1D6aSiypEvCuV +CZrjBSTgnG26xZwQ1ETTa2GAmRP3dfwSEvq4HEQndoN6 +7XghfirbcsDmiCHoE35HBg7shiE8K52rRWrHjomzq6wW +7rWJjjHFQCBU26yRBahbpgSt4ZG9HVr6fFcHTsgEksmn +Gunpcfp8zD5dw6QskZPH42h8cZKZ3cyw3mJvMRM415td +6oo4h4N5HnwhEDrQWnsjGZgrnxyDedz8a64q7vvXSJMz +VrH7cWTqQeUR1MrnHpq8Bn2GUjCV2tAb6egmdzvSS9u +3pC2hVMsyS16D96JDt6SvMEypgA4K2HSVYvwPGSqUtWu +24QaVvv59UFLXX9qLXBCyG4Q8Pk1dLvXriAFXDNcqgJD +14XxTdGQ5Ljcy2EBsbtMjDpc8mFcJvmNwyz5dg4PRrWh +37HTwaAYhYCxQiFyHRP2nNkPRxuHLUf2KVxCPQsavbZa +GDk6E1XEkw9DmGoKWbFcAMkiC9TNdS5qbLpxqHgaGUQX +GiqvFpqYc4DMs3ZcK4f3fejyrHqvgzo3bAJmgjuRgeXY +LKsmZijBnS3ytJN1MDhBnk7s4JzaubbHczWnUf6fwJd +2vYubChw2tjAWWsWqBQ1c5G7UPDem1hnmd9toyDNhuuv +GxbzPiERyNGJrFjHbPCBap6FKLq5i1tXfKHgHe4FuBDK +GhytFBDBcYdohFkCm1jjjZPkgmm9tuLSuaKK8w732wVq +3aQ1LDUJqq6bneBy27n6679GPYCQKHARHZNg42CRr7P1 +27DxKEgvRroneBmYxgTrWGM2MdQigtJzxV8J17RVPAMH +35SavqkF789cLtBxinFmzkTRKhwZLUCvwg6VtzdHFneQ +DALJUyvYmMSTrmrm4FVZZzdTuALCnYRVAYEhR9KA2nbE +54Ff9g1n7AJPdoYwtfhe8VFM4MELN4XNVb2CjL6agpr6 +HWLMQ9Ao8heo2v8CR1vW6XwaCrpXQH1dxjtgKZiWScdD +FgHczn7rsVs3V5ouUhpF11Sz1AVKoqcBZ9TQ2JJBud9m +CikCBavJNYeqLseH2cFR22fdKLoMHAZPtqBWcSe5PstR +HatSzHeM3gREStpEgcABT2y1wnBrXXeCQpKQ7K6Aucdp +EhAoQ996hRGb7LiwXE6Fo5SfDijTfNFsaS1FfVqW5EBy +GEWQxsvCUdWEiakJZRvr2cLnuBrcvXYRQxXckvx6dscS +GvTYdhWfF2tG46XUzoLXKxHDHA9PNVxuJ3rPhxXdepvi +2SKMCYNK4fiCs6iGZysX7GvSCS1VoR7F6okYfhTAzxAt +FpJEfJT6EiLHC2VzY8VKBKSHgUFTjsmiD2JX5YFqVKFy +2QtBfc2KyYf18SYYxhUe7rTShGjWaAmumJmZLLKmKjfm +EpwHHob8APJSXJVyqTNa5QJgv6DzLe3HgmzXYmKRvq5Q +5jHuCokHenky2nzZDjZs4WNoDkxStpnnppdP6C65Bcro +PSjogWREFUvB8LBpFC4L7vSp4fzVfnKBA1t4FzqNGPM +FmLMC9RVgwnHsyN6qWj3z4Z5dKnrzwyuREUQE7fXccDo +3CCPtkAxJ6GbziiUfuaWy7qroYFDZqh27QWLLPhpDReV +J7Bb5mYwHUrxeerFa8vgkknWRQaEfFV2sPLiDccLx1af +HMHFRgyEJD2gLob7ZScTYVcH6hXpBt4QVmYhnW27strc +HvFkSpeX2aS9Xn1pNCafYWA8nyJBUUbGW7Uzr4SmLZrG +J3BDns9YNVoo7K7v32B9mvb8quFxSiyv6iGN1nNSC3Bd +JBfdVaTQ3vrUDsCUHQGhWcxNQxdSNdtqvHq6aJ9JVhXL +GwDeatHpEjvWXhMR4G8GBix7tdihHtXNk4BVQv8bTKEV +FjJ6NN3w67hNUpqtcwBaL15a3CCUtT7SD9JRUniyazbM +GQNDAZw5u2DvztYgETEP49VDhU3ys1wdLiENRL6ZQmDj +4ZBbrepNVo3gk9Cw69TNs5kr1YE7AuiEa6BJLhZqkSBM +ALoXm64ci5A8DN3wmW82uxva1M3hFB7jykpfXmwBVUGH +7GMK6D8SEu8w4xnetnToV1hxhYcyNCsZhcBCmLPR2Ab5 +2TNe5MK8frTtPH57Bd7ToXPkMsXwv149eiavwm2EQt3f +FP24hkPud32h3quiu7FikgT1hF2Kh731YuHYZDb3f4sS +HFjF3qW71aJwgpZaCaXTM77s6uEPJxYxiLe3ZuJ3y5Fx +3YHZDKamaYnDAik4ya33KbtubtjBX4P7mJJKxHsH3Ufm +W4KVYNjWZmpzPNnjz6TYf6Rz6TQdzdiU6krXNNdJtSU +3ESB9YY5T7mU5G2dYHCk879RPS4ojK6roeWHcNhWpJAi +FUE4DJDnf5uv5nN1313usGNZCXwjSB55MJqRksZAoN5 +7rCaNrtMuSEJHACwDwT9Yp4qiK9BSChfzhqr6yQ81o1W +2s1Kz3QZNExQfpX4FNp1qVHox3qW44rnsfNjPwEjU2S5 +21rF4VgkVpUdUBfLPNbrhmL4pfsLdCMLoU2SdGSRoytT +BXG93di5okJUJD1HBkzcDdezGb2tURDjvVK7x1HdZLhZ +Ev2CAZ7BfbQrqBoFBqj7WZ9bDMqWJ9Gkwh7vBQ1yprME +ZPmB58WsjQbrbEFoQMAnPA45TDyVhf4jmr6WiuCuw16 +47VnvZQCt9ge7sGSQboxE6ih97vGPSThYwPNBdU2uNAB +3EHBJ4ayAqJeLHjhoKhaFMDFWV5FYwfMSZ9x1esmk9zP +2TjwaR1gTGGhbfnvVfLdtPD7UteokygTVNRciz5WdMT4 +FNnvTR3muPYCSd9Ua7ZQEWm3zgT84DnmX2vELTQkwwzd +Bh6Z9o1PYWpFnrybmiNK9kDUNigKd4uTvi6ocFhy2X9p +J5qdMxqmCPcRgHjcQxSCwcMRme7WJUUc6Cjd9ASDusTK +HgT5pymdHBTub4HYCnQymaK8Ug9JRKC4QUG4sPBCdFn7 +55KTSggK9HRH7o6qa7723416TmdUYHZ8jwn7KYq7UGVN +DqDLXMxAn8PEQE2rM2ed2yAwref6NeZWwfzFyfYTmQFh +NgvywdRDqVe1ENrBSeSpMZpXRuBdoLHqxJjie1roMyv +69ZdRCryCPRGaynTiJXdNZm4hoWhgeWkokWCXLdXnia3 +GHJz4DkNYH4T7fFznW5r2X4xGgjVN8xvobp9XR9a7MMZ +5GPTWYeHuXysxSsVXd4FVSbGea2mKJ1gx3E6gNfAuv6r +8PekUnPKJMadV5pcQRKeJ1qGzsvtrQVaueapSDVSTde3 +Fs4PkPAwga5z1Kw5pUn7gfrSUXHCPLhUdcATekZ47Ayz +GihPHiKA9BF6GahHU1kHq2h3TTK4Rv7vowyLgofQnKDJ +Gdm6AxhD4Qek4P2KEV3bgWx83Nz9xe9m4PCMEZETaKoX +Hpn28ebQ1NDD63V6MGqqDgrm8CfTwx2Ji8NDfHqH6jar +LBDRGXPF5ruko3Djwnfz6o2tsFqvEPfBbLKq86gATDN +Hem6LrBrs37BmhxwrGf4hPgh4i3zp3iT2kn18LxuCMgD +3mkCqJspRcrGUVtgHAJo6JJzZrVzu94wtLwGTxSFWfhn +J6km3QkFLcafupPM1Td5nsxQf84p9X9Z5QJVVjKUhWqP +D57fdcf6DAVtc92zHZZ4t93NAL1cShBWv61wdZH9d9D6 +HvsYyqCmwfcHNwy5d4FcTPF2AEmQ1gmyJNsC6vWaSuNh +5T4QYE1MLxYA44vDLFUCZa751oxvgQEhBTjk1B5qFEiH +EmH7tdFYh2eVqT2it6DcXaR694npx6jbFcy4M2KHexn +4AWBaiHauB4bvegL2YCqE5veq7roHcFXNywi2BbBwtJ3 +368sMRyNDJrWW17HuYxM23UjqHFkXFdsHokdnP6Bq6x4 +956n1et9vBcUB8i63hJoYSgyU2zRCvLYcsBCbx8iU2cB +HaQSaGBGbuugnNU2UhqzBCkHEt2pvibKTb3fWF7HFNgS +511EcSRHfN4znSsERp7THzmRCoEZL72NV5bGcsGbCNj3 +2FcDsT8stYTc8YM5DUwdPEUyc4nxUbHhjYXxDemZF9hm +HYPhJ3DTxHXxuX5fSticgcjoeZQXSzw4Ti488biXzjor +4YjeGzX2jJ3XCBQzn4yY3tBtatRVYShLfSf4tjot9EdZ +GZioWnen3JPLSLXrdE9fBDTqcKJurfagbfeW4LozCbbT +HVdDEnbLR2z6xDtXDGQ41X9u4RP86dLKmdoik649FkoT +FmCatnPae4QgQvzKSgtoFrJbxq6oxVobhgnV8yf4Thcq +GNTEQQqhFWfxA5P5HmTvo1NRfEmjwXspzbYaNeV4ub3v +H9aqxjMMSyKDSB2Bmb4jSoe2Ff1Thd8AmQaMHkFrfz3H +DpHVaqgWav6wxpz4EL59J79zS1yNyGJNWbouB7wEXsoU +2yDNUYnmrVGori4JfDem4G1nsZN4NMHkjvogvbtULnXG +2nZwAvaZLuzxVC358rjtW7BbcRGeXxJxqkJgcSNncfPS +H4bhCy6DvQ7WMdBjocpEwc5SkTpQQ7gaEpeKp3Vx2JzZ +8fFMiyJP8dbN6oEzvUXePcPCM4GYnemUCEXhMhDXgLDq +FYb8jmSxLaFLW3dCg44XPaU8aZdbzHXXRPMWh8M8wQNf +FiszJtLweREG7LPDMVozLkgdVe5YbLWvaikRW9pkWRwm +DPhD7Gvnu9FiWZwr2ehy18MmZgsYwQRWUS3aHg14vtWw +BK2vN7SmEAWVMNjkj2Qif1dLSGyW7PcDsm8P7wMCDbz +P2JVvpLurRVzMKf2GMv5G57X2GMBaMWRUmRba8bJdF8 +Gr1scKjdkwDiGR7msFd3F5xNvQMfPNH2PCrujRHjJ2Uv +FzsJHsDy9QwNtL4L6LzCRZv5L3fAQqr2BefPH6xC5Nw6 +3WCM21TR6pAEE2m6uZdfgsvBSV6QfDgeF7yVJawxjQCJ +6dFgj2pZm2c7Zzr8oDF3FybbfyoQPKS5mqVWL5AkAXdd +3uLy3Z6TbZ837ta2taJRpgEfvwZXjJcE6byFEiaU2Dc8 +GiCbnHiutToJPZMa5chZ4Xke6H6A9avrpjNvA3uuv2Rq +G39Z5g8ZLexV93Udrjo72woXjHVuDa2xkA5Maknseh6d +8vLKEvqzpqF1LN3TrEJkqHgXMyKJTxPEP6hQuUwzXuZw +Gaq7zW36mmTJcijRS3GmNBYzvCcjhKNuBQvSAPNrC5zf +J3H1MqPUy3VMEFsaJGZZCBs8M9nUfgFBjGeWQL1qynwd +5FrUDmPiPCxX8y4H7iE4bDAnJRydjnGfFKe4mpmYzBpG +5uQQSEbvGGREY3k3rjrsU9cy8f93uAodTRvPR2Xo3GXD +5RRkNtBisHLHDh8qdarT4QTMX5nsWbeTVRTSDzs4YGhA +4Yj8gMQamKYsvGcvAMJyDpZHeu83nDhiCaXUpVmTmx22 +JD1pG3DCnvkd3xZxsTJXdv8L2VR34wHMua52c7eYsSEw +GdYkNNXhAf3PwAKmcNKQ3tLE5stszhA4fKfq37bvAjTV +GF7vW3LMijdjnRZqzNtDoZHe7QXZbi1TG9vCCQcxgDED +Ho6konNDkSbnL5BMFwuNH69d3fySpRabzetX6H5fw9PS +5Vw65U3ocBoX1f5M5fS1esYbntPYJBAHJhhFqSU1W6sf +FxtSJ9Hg4aPPiZCsF1GScW8HTQNrTki4C19dZkZ137t9 +GAfQY243iFsF4C8wpMGzpQWbUfH8z86PmssgFpzwZF41 +GezvgTabdfqiEg8ByUxkKcEmb1GvDtSEMZy5ZaFEUmad +4FrWNHCQvuumCFtP6CBoEawipobPxxKku6K9z3Xhxend +2Xf1emegMTV78F3S5fnkLodtZmExYR9kkF8cu24e8KDG +3dapL9Bsq593EEsj8hs4ER5pS9tcD21H7XEc72TVX1ti +4RTPsbFdLbTsNYViUeEcTE18PuzQsLBFDFepFAYGkLn7 +ELaPUCdsjC2z1gkPUThNj6MU6V4DZdEwQxjRUs52f9UM +28SKLUUsE81S4rZKdLsj2HpLsrnTke3L3SW59t56eEpc +38dFVMUaSfzBknhodiHvsRoHezv93FSjyXG1yHE2Xn9E +4ev8LBjFRaSLQD6qHofbDTPrugeKBspS6VZPbRMc9kov +1Pt5djd8WYk3hCUvCVemuqxzwUjCcwBLXRYsR5B81sD +4PeiFcFhoErr6H2bqnCaSe6ZJcCmZriVfp3mPyEUTV4W +56tgjrLksxJggJUpJN8SePgwcD4WfESzGzmZdZx9pwGy +6KAToiqtaC4AYEE4gLiK8mhYsvJGGEUiSQVdLWbzYYLW +G5YhD1h6PWiApH9A6dKWFXHNsJUcjfyagfwH43WCZ1nX +5tivM2NpoDTSxXfe4rjMLGVKBsPTNXUksS5Qc5FQyJgm +9UUh1r1NZiHEXQXFt4pquN5ptHWnzM8RbUTxmuwdCRAB +8f8kJu4c8Eje5BqMv2ogGULh6anHQciC9D9q8DhgbqV +GKR2VuroqXBme7g1gZ2kzTpbHm4cVZMZTeZjPibXFY9W +L27kb2Y9udqiqDna4qTYdBJcuvpo9YmaX1EjAiTFk6Q +CikkZgyimdonHXydywbt5vKre9fFVcxWeh8HPnvt3nMm +2sa5Dve8mQfANTkgoHqafdRzVYB6Brmsxmcj49WCJ5r4 +7C3aLQZXXidsr9fUzJT2p7Wv5Joo6Xtw7RK5rNyhdggB +HCiejREwG8GbxSoEj5pX8hgy1U9ffAY834dgYRsrpdmA +FTDGo3Nnh1SFa9SjbjM9Px7AeuQF6eWrinmwshHTBGgi +R8TJw52PPD21zRWiskBi6TyzXzYP4819aTznSmCUh4F +9EeeyieRtpdMb8MLxzx1nWYGVPd2wb77QuLsxZXJnY6M +FpZLL7f9KGDgwb2SjW26mnP62RQfwyiVLLk7RLMJESCw +4kqsKAB2bWG9T2wcAkRWLrzH28H3e2KxCU9jmCicHtqS +29TSEAwn2W9fuzX1gr9kEdRxDHWsxELD7gfzKaNptEN7 +GDFPhvkzG7AADnsUMiWVn3mTpomnhP3MEfRjsXvqofXd +EML6KwDYccNZKfBC4U6Unce96a7e1kJNDaKJKx7DgZr +CXEkWRW2zByqRxJ3py3fHgBrGMcUUyS58Knp3WALesAK +6atvmJ7iR6JTaRav2F1SStoJ5P2wAWTGVnvxHF6C4YCo +HbDaLUPvfd6PGVAX9iXNsmegMFQXZwAsTPnSJ4CXLJQR +2BisWcqt5VnNM5Tkj6rjogiaZGbuzzLLfEQX4wG9atjB +3wtNCP6di4T5APjUyJkSysgMkkbCansrREy95PiQQrmD +SighykPy6jc6RjMtCXqxDBZkTeHqjYRbAFrqJBiKZWz +3eHKNusRKvPDbsLohKeqARNh3atmVvJr1hfxoqAuitD6 +9GvH796L8BpiozTLqTCDopPjzc44Qr9H2RXeMH3uB6SN +sbMczADo93w2YGED9yP4YtHH6tQbym2QxXbnBa71S8T +HbE2iPopYMDSdfFHdPNoF8D9ijMTGG6Yv5958RzHeTVb +AmyZeXFCvDgZsFoQsu22yE1AvcmamtvBPrtsZktK6RbE +4c33c3LZR7x4ayeyqm8NionbHqTRswZWHdvzXt9K1CzS +3mbhYHZzoAsRoPZYKXGWK7xVURaX4BAjkdsSKxuTHsFs +A7Jj3XXnL3N4GLGS2yqmheBu6KrNruKX6vwmmePzAdsU +2KpKWCxN6Z1ddZVz8skpQLP7Gyn4KAWqjRM2y1igVT4H +ArsdVsyH7CpAxUMm7CgHLFCd4iK4SVo34sQXeiQDDptY +GHkfUNefBDxu485zFDLFwQYkhapjZpS2x4m82GBY4ufL +9MYdo8PUNt2ruJMfShFPqGPSMffL3DoFgWAtRoHRuMKv +4PVmg7AiRzCNRDC2weo5DTvowWYf3RRLVySa8Mu9nkfD +55mb8nDNamFtttdHcV2wVZ7p62raFYa7BtScJUyEWduJ +D9T55pfqArFZTZGruYoiZpBT5SCtuV7wNmtW8SnfTj9h +FTgyyPVyGntKqtjdtH5BVozZbFxQyGYZ8wkpgSDmLtW6 +76x5FFbEcJsh6jUTFe59amuqvTsZDsjekRNtkyY4DUqJ +BdpHAJJmcp1JszLXfT5NX2rEiBTvpuqi6peZPgwtkzJ1 +9jnqz11f5oHwfm53v4ZZcVuwqxrCETNUhnq8yLXMQjYQ +FgUDv3U4WLProHeAMUuf2pvTtdQJkxYiHNTCerwYg3RB +7RALWUPFguVJp5RgJKvqeCofDLSLQaSPT7yGS3x5a5A4 +Gt5Q4wWt9YbkftZmWYn5Dupo29RJZL6d2PZW9CqDNo5o +FHL54ro7NV9nTfvhKN15y7VJc4ir4p3TxG97fP4xTbKZ +HqK2QsyJHkgU7d7z7GTpwUdC7pJTGX6FkUJhJmiswozu +2DGo44X4DnvNjYyBXSqNJLXikkmBfju9JqvrNs7UMnTP +CB7SCTQ51MLvU7GUqhdj7dzF8ko1ZADsUQqNzrPFjjko +3cvQh5A9Suqd7iwZ7aiQXxvpvACffCD84vUfcCPn4j1b +27YevUaQGqMsdWwnSkkhQdLhbdbd2quRsgSP22pQRTfh +5Fo5xiGHRajyA6aMGBiG8HpLxGF4fxhxsYM1FidmA8Xs +34Fgyxtjn7W1GPBAD86cJHU61fYuAz9K78q5vjJJPdBV +9gt3ogGwf1Ke9ksJp7xsTVu9ZehfFnQB3sCBo1KC4V3L +CvnMJntf9N3JxjrxQ4aaehL4aS8dvKqf1mZhrXWBYiej +3FqJHHTFn7i9Lbz97aH2gMM9w7BC2ScacV3x9Zp4AzfR +AGYzuW61JB6hf4ysX5fPc9UgzfbYhvB48PvT73odK3ww +Ft2anNNJTQ9JLaHoviJQaqFNx4bp9WgzfEUjKZpa76ks +G6VSWF6kCmDuoRw8oNPfQNnJSxczvbi2TKJAz1VfVk2o +2Tfoe5sewRE73qATPhDmaDf9cBtqEPcYiTmE49s3xNuJ +J1xmfse99AA9Eowkh1RLV3P3iBegoVxHYWP1GEYprQpM +2mod9cK49KBS4LvVTHvfZwoi3mW3PqwGnXUdfgbbuA11 +Bt8k9PtHsxHKxgtHWYaYBXoMA7grxHKeLixH9BQQLPVx +5SYPnqMQSGeq194rz8sjE31HxvRqzGSGEVFSfrEjbbPT +3eKacuDoTV6s875tXcNLTbAWorJF4HPDWnc4b1ugTrTN +76pDHimnsLK2rsY1i9X8Bxfm6RiQWVR8LMBywdqmi4ew +XBSqffTs9fGkJchdJbf7spuAXu92arAv7mquuiYjFSv +ECpauuMnBBUa6Y3k38ET3nTC8XgQPLoNj3iSWH716nt9 +5Xs61RSedXTDDBmRFBek6ZpY34RmQ6Qhdb3LcSwBPoDR +HEkokc3Za3L948T2uy911QbWAxUvFWVVu8YpnS6A851b +BZo37iJjojkJwLW3Hsmg2aG4uzPbaWWQpDwhtXtJm6SE +2HFYhPrzDH5jKAumYSMahqiNeFAFPn9qqUh3ad2C3KFu +9BXSBmrKUKazxgiPGBtAKK9735EGs22Moh3oSXiVKo7P +HLXq2bKUzdtqaP3UrYyQobuMGtnVV11zjk3Mp3EFf7L3 +GP2YHA8Sq7wqz87MDJAB2Sirw92K1B3KSBf5ux8BKb2h +G6novgQV4fWUWrxnX7engKvG4r3PnWjofbhRsAcFdJ6J +HQxevGZoTpbv5NJXyxpgL9WbRHn1ANoVJCcJkCki3Wjt +CArzJfNx4gNWzFKZVvardTk3TFB6CVCtdR6KyHA2xGHm +3Kqo7F49Fg4mccNxsX7u6M59NZZDCmRoq17nThP7CntR +4R2y6iu1ppW7Vb78via59SMWbrknPa5zVYYgGTasBbA9 +8qZqcXihbecRujcRas1zxJJDRyiVztNVhKYdmavAB7y9 +3z9kCU29TbuyPaCsZJZ1epBEdsv33nTbeC3XLD15V8wv +48WDvpyxYyJgb5m4UihR9ZkcmE53H43vQDhybRJWwRiE +BokU9TvdaJt2QBdxoLmyuDVAv59AvrLXhUVP8i7smnU8 +3arv9DmMoCFJkiYCbMdBYyAcLcYy5bVRVdJKDTzZRnx3 +5Hhk7xkvcSPThvKVstFjuA3CHR5KXeGZnEszUS2KvU83 +3TfGExWWQVdhEhAbd6oq6hiJXuiU7jL4y674FGdGKNwP +CUZDhc3vC7KJGYkCUEBjJtEHVXbGb58LQRNeoYudoeQh +27tMorjKGjPqkmvqe1J3P37e2xq8wHiFNRnF1nciUULJ +GQqvsQ7UA8ctwNcgtmF9kgWP5Nj2J3VRHepheS8ADg56 +GYPKzirGgSrPnZNJJ8nr9LnZbxNEyCSeEXx4Qomgdikv +GtWpiSrw1A7foK6MoJVrcsiu63wWzFqPMACXCj7vCoMd +8hdWAgwZ1gVJfViexF8t8E6PAFT4pKoC6NU1bQScDvEy +2zjvYbxHJbjBjbGY6aEVvnasfC5pTiTENdoDCoAsZQ9w +iiSy5JDdkUbfDx1K186BC9T795fZrThTetucr4J1Ro6 +HzH3fLvDwBtGuTj56imYbZkNFgTK6yyj9g3rbUAd8Zgo +4GjXrVJ5XD9zUiwdiJbTtuM3wWGhBT1yZCHSNu6FL6VN +GLwNayiejBfCYjuTpNBxk2v49ZMT9PB3hb3u388zCzsM +58NWGz2KVHE1ZquRUMYrLAjnThspFGd5FuZsMRk5ktY7 +D2y4eYKHkzNzbn52B5Gd3Qp84gACuNuF5j2JJoBKtK6i +47ZDz39R9ssbaqXNVAG8o4iDLe8hJczDqZNPdBau8JxB +9K2bVjF8R86MV1JRahAVqGVwSk9kXbrEBS9tuVVsWpU +4JSF5Ea6ZMAXMRsEKdVgETHG3KGTLnCneEz79nfDcZsz +HSqya7CWumXWFemwJTNRsgUfnnSAbkRpxywRxhdwVu2m +4MZwfm52nB49Yez8H1hqPReRXRJTb8b7J7qLtSpfB1mD +22ADbXjGQ2fWovGsh5Su13Fwv6uE4uNjZmTxhMhgEGD8 +JCPk958VdRkyQhPjhUFY9FQwUNZ7aGE1hPiihKi4Kzwn +FpdE8JQwVqT18HDwQMvCrVRCYQoZ4hja9GTu3F85nQqB +DCBDT5D9KB9BtzcvWwWu1jtyc877G5oWPLPtmwDZgX5f +B8aGZCAwGKA34zdv4zV3cJ5aa8e9ScmzShLSU1FHrsS9 +7JpruSYomiSdLqXRXLu3TB31NN4KGdJb26bZwACtiUPZ +388RLZDCkhKyQuYveVbPUv992jznhY1rEx4pPVHmksyN +3j2BSe8PXtqDBDr9b8SFtPUHmvhWpziyYuCzoCVkhUXs +BhX2tu4Qm3TwMUodWjt5cwY4s5mvHaJxwn66ZtRWvvG9 +FmpHdFP5fMZeMQZYUoizwsn34tcd1FBMhFRuei9nALSD +43rQU8ZwzVfUXwoXdo62YsFV2g4PNbuVnZJQFBV9tGJb +FafQM6LZ5DHSxRNvpy5Vr7PUmUgWwFQHse9iykxbE4Rr +FVvXB9SaJfch4R2pg76xCSPjKDsGuHRDMpVP4NAsyje8 +4ApHr4MDuGJYobPBmcYx7aVdxxkEe7oZR84dU5YCfzoX +J8hrFNGjNHV7tieNNUg44hP5bPBVpirSNXuYqrWUb99Q +AXzjw1fEgc8HZgYQyoaWDdxNcvgABXoeeH5wUJBrKUP +2XjgopCamoCpBMgE5Y2A6ccRYN5nuuM9419KaG3oXsZ2 +25xsrvxJeuXb4ynGGF5yeQxeWLs5kjKfwACTC5cYfnmG +3oJGEpg2n5fxBTXZRCdaQUfjpdb8AVAgUWcqvQPSEf3r +G9uicUQmDPHcDmGNsg8Undmpyhn3kDvcQZzMwz5sDzKf +GGG5TF9L5aCcJo6jGSCYzCrbBVYDp7RF9EKjmXc5oxfV +FmE6tvYwnY9occFx1y6EcLXJGNryn96nhcY1PzY145an +2G4CVb4aNsWYbfJHkAui6cj9teg4o2BkUcbgr5QtrV5W +HSy8jQStZ79RvvYhjeGsoWJx9BgtBHApkkxhju5ErJiD +5bKb9ADyqYpRy3Z8jpnjoqAkmZuJ2sWShCTkHHq2AMy8 +3pf8AVw8R7e5nDCBwLmhpMimeqqSjKS62gJgWM852gR1 +FWF72ZpEoweymV2arqp1LxsW6hx1RWr9WPU9g16J6HW1 +Gu7ZgftTm7LU2EPCzx9EjaF7VoPukPDtfJ8WDwMQ5YYb +3Yr71Z3yt2U8AzekwQwSVxEQaoKg75LghshnMBs3U9HY +6RihEeBr7NwDL2JzvYDKtRys1cnFGZKeSCqdDNSyn3iH +77ri87LSiorPshvy8SjsavHzE1Xe623NkU9ocmFpo5qZ +9SEdnGcKaxr7hwbGtjkUPM2Q4dGqUrWHiV5NBkKBKoKu +2Sc6i5VsFDhJ6mbdxQ82uvpL9ytKh2h51b8NAtz6Mab4 +HrYhRcphmzTegLMdk74idJktpztw72YUGxEU1HNxR7ku +J8BHF3j82kpQF4R74r49xNjd1p1R9raAeVZBq7GKeCr4 +Fw4qu2fqz49WZbfNFrrGLKAToHSrk8Yn1AKx8KDkMwgT +HKJUDSpHGvK2bXSYsezTUD2JoFiiVv5xRAVGHfSXBPsx +FmFxQkwKncJs9c3nK3AtWFfi58h7WYWPze4fJ7mKtLHQ +yHdeGBRZdP8Y5bz7qn1bVwgQtZQAyLrE5haFvqshqzm +3KnJ6NVn1c6poagiyyfSXXHvoxCrkkkAveSKcHWS8XEC +H1Qx8HrVcRZULLtWhynVQYpRK7nara6pwfthvrxqWzA2 +4idEw1CvqsDfEEZtCKL1PYATeexwCddwb4JeZjH1Bggk +EJnYAXLrB2tNPiz857GyWqBgGq8SyKjkbyS9oFV4zjZV +Gd9Pm1mZrvj8KGhBUUfFCu6wu7MUkAAGoBgxZbMSarnF +6s8YQ3qmfxufPGFiB7nBG6ZSvzHdStKn7W9UK4LmGQ1D +CB9TBE6qoHaVuhpE4VvQoj79AasbU2UKefYcQs9Hy6kD +Co8NbVHvVymsF4kWyKfMcWQMqtynTNqiCieaHn7fKbxP +4XnhTZyjtUtWt1Unda1TkXc99kC2UKnjEog2Wsu7Rq6j +592zJPGU7CeMaD37VGsq9SuSnSaz58euc7KpyCAUTLDJ +txveTjcWCs8hQnVHffnHiZScrSSSu84G24CSUYDV2NL +GNx1RCuoo2y5jFehe8jbq455MtNue3aYMw3mFUVnB5y1 +4x9fMsoXSbzZieh9KGjkzvBT4nPkYE6KhQeyL4g52Uz1 +3AM9fRDzmrMZqKwdF59ysiYgivgmixSKfNaCHQKzn9GS +BE7QUNUABocE6HpUxA1fA25WnH6YDZ7JPkhEiUvwvhMf +B7zx4Q52Vuwf15GtF7SKaUZJv5LGdN6UhxJ2WDimHAEn +J1gzoRb52gi95vhfkvMug4Uyv61jSHZEf4ueek44PJga +7AYHfVUAGnM49n4EJdM1VBhQTNLETGUCuwysRf22AbqH +3jZ3S3EVHe1E51HHVT5b3fYeVerRKoqV71N2UJCkm6dx +3eokeJh9aKxRkW8AHkH79BTqfaJWMuVTRJ7h6qYX7Y6N +4d6KjojFbrTd8RNswcQspFNESGTxzscY19HXu2yAEQ4P +8Z99jdVnEEyifcRm2ebywPcfnapgoMkTzfXNLrNfZtPY +2pXKQY4kmkYg37B8EaWtKtTm3GJMy2RrSaVJyK5ZDgYB +3MpwaN5q8QyqkgyidhwrD1vUbTHB9Jq6wBgrfPKJPn29 +6BLWRkKHicrWedNCceQvLZe4njjBAcegqXH1XA4TD995 +Cj81Vn8zhNCGknBHHuscqAVP7kLCxNAL1mVjHTa2V6Fe +2nhmuUo8WVYZLFTjoaxN7MtaawRcvxjZHQGA1nV1Zniu +E2aFZLxwEGjxmRzsQBZrNonhLEN5ZQ3RjFXwRCtRyNGV +2KPBizrWX12B5GAWaMix5XX5tKwSPvpnWwUJYLuEWQDH +GjJYvqhHodEti3wbdBSTVcwHc9HCxW9cQABA1j9Ub9g1 +72T3GKU5v6J3GNo1mDd6fiw2jKsz9MUuShhCmTDLkTxj +YNMgPMth3SZvCKJLgZiFBhSzZfPPBBBHRkCn6W3P9ui +FodRpGUemHySmMmCFZ9FY5nVsYcH7wiHY5am1kRy1vJw +GAVXQvQK4bF56eawMhHdjqxk4zJiAnwUNNotzLk83Asc +4hohQCksHc4LNumEpN4ivKnM9yxVCAoA2yFUghP5G5Ms +2fvt2HkgtFPkenFeat8jNELX2CZH842dAEQLEk8Kt3oX +9uApcfgFMzD4xqo5PPVJrNZ3KHTWMsTc4nKXJqp3Y26F +ewU1C7gG8J2dko9hQmJJrUyvs9uMXrRFzMmNcZGTDUS +HZhyU8Vpgy8fYrr2CvF6AU53wiAbd9cdyNPos98CwXmX +tDh5TuUEikpsqwHPkimBtMQJPYJPmC1devvMYEUejqe +2RbM5pdbuzmEfPE4zpspw2YtU1Xhjp5zkVywEwT59ChA +2W7SE1ZV9XV7mJQ8pPXjyKaSPpMzpXj5CW8uKXRvB6XW +3WyzYQWvVezDuqMCFysk4MUpw3jTYPqKGJ68C49eJ8vA +wME2YkkCSgf4tEDjr6XShfa5JpfgKmvTGfrzEUv1kyJ +4WxKGwLQiGc8FJZAa7dtdVLEyPYtUTCGcaNuRx4oZvfj +HuD2tjLDVD3VZTeXwh5ske8ajAQKABTgGyZLZorANUT1 +4WhpjjtUoEcHT5hzw2tk3o1qFY53JxZuWXiR4sTyvcAG +J1qfj3pRhfSZmh9XoJt3496mykPjG6Qo7UiUhG3YJNVx +vzuteKchotdPXJ2E4w6Xcns3PsZnmYhi9EQFoVpqcZo +4WWTKet9FDFXVa3X8JakzKMtSxuRSxX3YP7NuBQGLBWe +BnmCorYbE12GGtYsxKjp53sf5EHpWhtPFfE5x6KihfQW +5gNKG32d4ZPi3tYRFVLQbYVqbEZjeHHwm5NdMdzdwh9 +7RXjHBurZBCr7AzD4WyQ3TjnYccSV2EiX3c5PR2N7X6j +77TvDDXXScKoqZk8WjXY1U1vCFtUrwVxXkavRhiz5rnk +4jSQid5joocnw7UFY3oHkSnBZWvaVsHGpF5ociTarhCN +8iU95hvm1c2Xq2MDUt1HrE7Vu8aU6gsqN5SkD5BbAjHf +HZ9BVhvMYgE3Ba6jyDjw4VwvgqSZEQTtooMQCqDvfpXE +DCh2mfJh5ZodKTcTfuVzv6wgPtvQWtenJVeXkMBEX2P6 +42rZUN99PSkqEh79wvQF6Mp21BcS36aFRPnzF4K1vaHs +HUgdaMbuaTp4X2e1neLjfQWkSEKZpWBDoPuWJwxycMkx +HvSi3CH7PtSPjormfG62hM6rwC25zVnbEBTorZJ1HfxU +3Pd742rqChqB8Whfkm1GwKLKiM3972AWrnhjrougkU6t +ADcXvS2SU6KSnwn3wAHB2Ts3pbJGR9PePa1r4hohQMcu +DMhkZFUaZNzoQS8FKnToRRTtuKYMh188PXxCoeU6Wzji +41tLM4P1xYvrmKySX81x5DJeozCZkk7YMkgaTihojD9S +GfQmeTbZD1B848Nq5MhDjPNmwVMMy8QmDHfYVmTJfmJq +GmFHEvSCnxXWGKM6mk1saNYCpWG46HfVprJTZCYdWskB +2C2QLvBdoSm4oFzSMoyGoTp6xCq1K1XAFg55ssjroM28 +H4HWf9wkv3iyHjpvEtJMUx2NerAWrR4niGEeYEgdyVSZ +9fNSsfbUkEBCAtvN9KJiMM2ekoB2BPp7vxukgXdUh4BE +CuQ3JG6AkUgS63T2PD5WWJDLvndviyxhAmaXyxhGwFDy +FdtDU7iYaS3EjrBs5xX3UBcrabEArooCTF9C6cVfk9Lp +4TjYpwhBeYvuudoSHJ3WYS7niKYffnijgxaoLmdt2h3U +GLz3JDBuYLbJUvRvvfB8u8ryZPu6gebVMAs4R6j1YsKU +5ERZ16v9MnQYa1Vbdna4fHA6aXHaS38oiyGpxyR4pZtc +9KgdQCRaAMVBPHLpJfTTWzUg8ZYxSNZWPp1vpKfcTiux +6LTQ7CQpTV7AQBc1QgHTYiv5EEZ6XcWDeQGapyhwfX2F +3gHqsWvsq6nQ8xTFFUhKpFDZC9J1U7p8mAbPjcBjYJx8 +4LaMA5RmZsEpMMZWnDkwmad6Z6kq5rPPgPUsYHFJu15D +Fof8fjoDKcSPz3rRLFoM5eyX4gUGoiVCnZWU2TzjEgqQ +FLmWkEPicSQEMKfX9YNLpueiqSKad9wvA2wodSMMFE4x +7pdtgRbAHKGybZM38rZi8q8htSQKGcE1qwHvnkWPhb2q +KVCnV1283dADoSSSH13YDcp4kPWbkCoptnGXyXfu8oy +FevFEkCzCrh9pHv6ZjYzQ4jusePZdntZ8xy1adqyQ3DP +9hXdzUjsakeEbKhgJHiooTtFWHMxxDg7jSwZBqqdGm6n +3uxKVrisSCsmHSjzoSrxksj8TZL28vtU3FWZTYA3zLGn +3oSTY6pca1UVRysko7aQT7WJg2YDjspV5gJASNg3t3Tw +4htEuqdFzZfBdCMErG9Uy2icKM43kXbJ1cCfBkAU1sNo +HzrrhNhaGM3HceCpXHhePHvvTYXZjM1qeG9u8MkfVmVs +96BPrM5HNntU1YmqZbNDpmr94eKy8K2g8EDvT7T9DCEg +E2s6vgiHqqGcQum6a6PQvxPgCyjCTvwoEjPYUYxRLmCz +HfvpVw3Lt3BFQawrtorMpp1C12NRGbxmx2Fpmgye9rbq +DPB78RCavBTnqhRZGK2L3HisDu9pKN685EURwqTCNUta +51cUSqrbsa5KaKE6RSemUHustsdwbTa9em8TvNdnMig5 +4rWooBTfyZS17yGLPVuNTVY9yihvuJ6FowHvDszzxi4k +3uksJdBbjWRxe2gpJ3Bj4Cgp2QUXZ2iaVKaE8NogikkK +3my7hfoHqvkj5qg3JUkLkiQ2tvvHXAY37upbjibiPL8C +AVwX3C9vhpZLz8jsDCd4dgLku2xC9C2gLnfGrVqHXGN4 +A8Xqm8jf4Jnt92qgJCW3dE1msy3eBnaq3NUgWUYtttkX +3cpnfTYiHgGAXbroipC4oooxDwVoGntNrU7iHRurHMsc +F2D2xkvnSV7ztfzVJS4E4xJkJyXeC3YAC7S5cfhWQwT1 +4M83r6jKBNeEToMTrDNcHrvaobyZCFNMXGnLx3XRaNxE +DrNK9NSCYhSAKfPnP9s3J3At5Ps9YX5xKKPbego4aKiu +8Q9jCK92vrAi6fFu2NxvsuvoRbEGJGjuAsWdXAqmhGdd +4ENnNnfmwkx5LXkhwwh5EbT5wSLatHGYXnuqHKbhX4qL +3mrXyRLiSPDHJpyVLacwzDvS8L3Hm14iwzEFM8k7XkWo +2fuVhZrW44MRHez5XCYUnF3S1C2f4G9rCStVovYX13xg +71ARZT1JNncxqQKH9hw1qQopQUd6jpbSS1jaPdn36EQ7 +5hA6JzDsPqEru2exktaE2gxVJD1tpBAvHCEkA2yE8d9j +3BK6sRfHDhVXiG6zuc4STsNSqXCtq6AMBbc1FX3dfn23 +3USKLhcmfvriq8AgFSQtYvqL1nGHVFtEUDwMXQ3MjXpp +3XTScCngpknr7doBF858p6Vna5JE5p9vV6JtgCiziJqW +5cqASjsD3cD6rh4mpK6yKbG3rc4aqgEpGtCJAerPyvcn +JCNP3oV2kP9nt6o57X11hdbbCvrHjnTq5nxZ4aJUew1V +4moP7NtqW8NcL3A32A1Eg3hbba6tL7K8Rb8NozJpA7FK +3AvSGzcd31MiMDsyYMMhH9JnbXcLg2RRGPpCDrnMh8BL +FTwKSMt6giMq69CS2DNXkzZC3f2kVdEWVUwSRMANA5JG +ErT322QewMR9reVcfMfcLSuUwmRoQaFFV7MyHW1xCSke +Exhxd5DH4uVNyzqXK6uYd4KBzmJf3gyY52ZkhX2g5bPB +GreABXv6FSDej3VvF4dVHhpq55cvd9WjVYZEGSEiUzBr +6R26gmcCLWvUVK6zdyFDWtZn7qeuuUBbbEQjLkZaQKfD +3tXRPJNNrd6div8WutfyNVXF8ZHVvh86vDfXYA339qsn +4fkNvgmNpHLXrT9mr35oz2sL3YSonWk6pN4CXqgoxoQX +J9a2gKFNWySbxSFKkaJwDo1SDAsoFkFKZZwXFeZGmxMd +4PhZfebeQcmDck8kpWSg4CzYXLitMX5pAmQ9F3NMg9Wm +38mTT1eCkdxwTibuXYBfHXNJvHva47yQFfmcgmsPSmzs +4Q37zgQyvastejvCLLBmCBiYTnqLmFLbpQHoRqf2d7LG +G3VhALcAKgNQqxf5MSWShFhpG3bf8n9nwkDongqaXf7u +Gk8eatW29TzYX5cKJ4nZT9ynh6TLGqKf2HWCSc3CY8um +12Vuq7tEsbqjBgDqphHiUFeWyDN4vBMzgXQoghXPdaHQ +J19oktV4ggzQGqWs8h8Ge6u81in1m5ENt3YC9GcQH237 +Fjy1qen8rm1sSwCkqdj8xFG4CowKH2AiVVUaF2mtcWnU +HAn9ZbmfkN6XDGmiUmPN2EvsgnZkQdLTksiHRJHr9rmy +516fqa7buaUHsYhXdQVNC7WY9h23qUvAHB89qmWu7RDG +DNCGDfwCVyYJ2wKFxR1hJpZcvFtRFqPrqYRfyzxqo3SG +5JLpc8Gddr9aqebF7q7EyR884qtCaURAYWUxEsw7Z78j +2uEcqpU4K1rm3e4RfQ6MhnJUnFaktggo5JapzHzR3B3q +45wvv29QDMFWz1jBgUoNVhm37E7eTPFBuqWKfiy4b1r6 +HkB36u7StPU8QRpPTXCT91nyzyocLFL8nmGRhtQBjH57 +H729b31CNbqs4LAPJFQAGUvSUBjbSzRx8mL7ymsCTJyb +4gwKGd9CfZqZftPBV6P84fHojyJ3uCgNYSjqY2Z4Jytq +46mBFdQfxAJMaaERNXP916ZCXzcof2U9LBHYAzaLQkiA +FYRFoocxHiqiBbjGegAqaRV4KrtNw2xP4VW3io5pTwq7 +F6tDW4v8EkFNeQ73EYv8ypzYJpTNiVND3iJxNFGVwWj7 +GeuyrCe5g5VPEzeb4TKhPXEu6giFeeJXD2WanaSVNmMT +2cjMYpfoRXFAHCCUvK6TYcYKYpzwSYB2dwRz6ftRWLaK +CP5xB3RUuDFMMeyECuQGuAdNiAGfvCkmY883GSYWKrw4 +H9qwkCj6V7tbcuY9Dcm6nw2Wg8tbpFrS9SuiVSwN7mcZ +bkw9BiqXNEXPKw7dSA9nCy7EoynQb2q7k9ZhKZKGnAu +4Ac6DLumA3o1e5o629izg4BtPaSpq41p8CFNd1CuCz6S +GUXp3HKfHgb3QJFjdXgeagdbVsD7mLqVHh5e3TSSjDYH +FTNJbKseNveQTstDyQf8joTLS6SGaxxV11aW49etHa2p +23rPmXzoLf7dfYxrULUTkfWFpXqbW6pDLU7QVd15LUR5 +3HYH8wB3Y8tREdqmm6pjUh1yLmQHXrbCgwFCEUBh6ntL +Bv2fDFhQDRz5SK1LAQ6c51kpo3aaNW6AW9iBH8TJTrw +GFnhEtdvJfRpmEgB3tM6137hNyiWRGnnZzCVamNSChhD +H4kBVMrJpg74tPZwCBajFmUbVyhH22NAaWkcPrfrmqPp +5MAD21doESjsH3WCprkwLbrrwVHZ2vFAzM29w9vkbsT8 +34R4CSASUYHsHVNBi6Tj2Xx6CTtYpERGUPDY6yYnCYzk +FsrFaBrJAZyFx3WzbH5PdS92D24QrPtzQvquybYizpeb +37z4tsEeyPQCWCSMAq4JZYuoVmwMhqEtUWLt3cZ2KeHn +Gd99WgDsRPr4g1Jmd3zZSxEXDtKxL82QzLZ3VPNf4Tpe +EbSdjfymUascbWDR4RUrxUusC9mQXcnYvaFrAJk3fMAR +FqB47Xs9PXjErHTXuD1RMyDe2GfgezDwfERSNxfGs8Jv +DSbDbMdPd8b7cWUkHTtkkgVVGNdqe2f1m8yaiX8rBbK2 +CRX3CgsN1PZ8CZXyHkCyUgV9hiB4ZYfAXwhY5t9cZwxJ +H5adrNWzLzAL8bxntuRXaAMMvHtCZbEZyM75pwcoTV9X +Bg2ddYxvud1NUCfww3REFF955Gk6rF7FnEbDCg8R87Np +78gqc3RZe6uiSFoTjPYGkjAK8Y7VJeTJASKLcWRSNJHc +BHqiBQgSQMvNtPapbZdbpwyU4U2zNsjFirgQrofLpPR4 +2BRDCRM78FD5xd3h5oi89Sfc5acMTzgUGCn5TPuq1dwL +31a9uuKzhnRFEQ3v4kLQzvViFYSsCfgxymuKwvgLsqNV +8Cgusd1WxM4cbuf8McquX7Qr2yG4iSn2QXGBb4H8jczC +7v77Nc1YXc6TSoCKQFxvzBpNfFWMX3rbwDc5g2eWk9Qi +BXt6iUQnDWkggdwEmjSJfSe331DAYt5mVFwmDmawcRj +2VuE4jQLgeeEruErmvXG5Tdzyeg4s8oAaCrLtzh4bQb8 +HuyQRtP5jcDA8A8uriNgbPPCTHqmhA1noxMxUkHoj8dS +7gk2pjC9orSQwEYaiwg9jEiM9L5zXkKU956Fhg2MTdDc +GhQyekHuW9PfXC8oWpWBHyqnMsTRgqY59fJWYuJV5Hzq +HDPigAhcavRaf92W6ziP44dnrx9XkHtqnDLBdoFsvFdK +HZdZkKp8Yw7vnTps4Hwocat2XPr1ZxutvLSis4aQFhMs +49JBdMxsKXAktLzDaAY8rn4d9SsuTyTsS4fNpKLWbXsJ +3eV8ri7c7soLZredJ8sYrJubXoWkLZkGuvvAZ3E8E634 +HHKyBLxbhRpsHVAdnmpbg4ShN7DGDHthVg3RL5WUZRdJ +HgLZbXCcAFuFN1NAKFDSKbUdFFgjNopvMRcPFAEEzuDE +9wWpABRawY9vrhhaBP13RDYKJXeQLKVBNCZMe5Q4KCkb +6Rfta2n9SB2vc69oiHgN3cEo3MieskcXzYD52eJ2BrSD +21aVLWNx1QM1yLEcKniDtwUCgxHUhDLMhiwDYhhZb4Ag +4w5VUVxmE673K3dbUWoZVZcyANQBK59ZV6PEcPXUWkAf +GniwvhwWuNYWabrXCZwwtCggHij2PMz6HGqEHkNYPi2m +43p65kVCiWiasgdoMm5XNAmAhNH5VXXhmUxkS9Ph1C2N +FzUw3KPYguyLUw8CjKz2dySvhG6CWnvua1xkgiEUd1a1 +8pWQBUKeeWB8qedUo1oThM1GR9oedkuM7cAM7ErqymvN +7oRQsS2jpdYUfzkNUAeELwoo5tAu6T6cW7ZSQfh6FM84 +39kNJMPUGKWtA8dqSCHQweuTNcJsEZ2GpSLgNVL1JoRh +gKdUWKBo1qEmwmTuySv6fancmAdkTwiY5zUpk1YGsai +2ioEZr8aEhmVDAAiD69pCoTQHL5x4m6ZuiuNfi4wLayC +Bo54GUAcFgSe6KrVuk2c3xePXR18A3hsSVASCX4Xx7Yn +4uoAGTaXt1UuU6ypJ4VC6devQYVv6FdSTDBEXvdtC3jd +4VivGKazFM8pgxdXhW5W8j8R3UQY2gYoZiTr1e4SDFVH +HkU36Ykzak7TTMQskc1E8XRCZKfFgsWy9pYiCkNP6FdL +J6cjansVweJjYrbu3asiWZnQ2cRcA8ssUF2UufbD4eeq +GWHpTW1E315aA8DxwyEzCq8XYgvGnTZwdqD8ysq7Hak +GtPx86PykVA4dXq3PMPLQyZfd6w86RFMZ7hVUWMtVLho +2uDeWhDFdDT8XD48mcYqwWxvotRAV53mmkBrTJjLAeCf +5WgXcA3dZgjAyR8wf1FzajmK1jZ5HNBF19MswmF8v4mf +4b17P7jFEfj4sKsPaVj2NusotZBopmuypoSdr1LtJiHz +GZwvbjMqMqeXENcWpxtWDzmKzxn26GjTVzgTuC2EfuU1 +9f4b9wDvutdWbvmBNfZF3bLxSCBTpz2zCTYDV4oQqeXN +4ZKukhgtUJCS8yugxD39FEuyiMEibeaKua71NhMHS4Nx +GniraB8F1TvR2xiJA4PJb12dmzxUgUT8XeUPBy1FUq44 +2w2hfu5LzZ5v4aCcAAeoQK1pNpqqxAj7LPsgkdBkwa8Y +2pjaNN2eXpirkDndw3RhdQpd2f7CqoaoUBYai3afjmcU +J2Vg4eopTofW3GPHjjSTFcSUFM7uKuNRaUy49SnoY4iF +5Jf1Lk6GgnP56pFspqeVza28yKDdz774fKBsvXanRMUq +nMLwpxV2qebRgYcFNhueDHLzr2HVtqckJJUiPbhUSWH +5B8nkUGzAo5Hw6odbdvi2vNsfoyVkFNNMWoyFzSxnzKS +SAtWyY7QZEdd8Dun1W7rZSLEHUDqKTRuiff4BpbFvDi +G7bTd5ZVQJwyDfa7qbBCMAZq3asbEBfHMNKjD1fQ5V2J +dRZVVUXp9SxSBGf6DtFzda3mWrz2MRNNPePvoSSXAfB +STgmvqoMmYQ4ptMNiGL6R4MuwixHUpDby6rL3Bg1mmG +FWfS6J26JJCdJz4W5u3VMR7LfpD45rv2grLZhRkoTHRi +CLgPCim9V5bsWbCJPebRgcgqo3yfW2meTyRXerH3rsw4 +FdatkzC8ribR9dxc63B8XbC5Gy5SiEB6vwUVr4cq7Fuy +3uodYdM9TZfdPx1Kd24BqUqarELH1V7sBs5VeZHfBTZc +CtUEZx2vYbi8vNwFZQ8xb8F2iGMJ44PaW4zDJ3HLSNTS +2Sw7CqpWRWyWTWn9PKNntHaNB8wq2LsL8npinzai2tmu +JB4utVppfydQobx7ikQsX9GMMKFwUASjwyBonEbYozXt +FzjpQFfvNang1sgmKdKu5sYe2fBgH7o8goTxmKFrKD33 +HMPdzaMNQtNCZbdgb4tzj4wnrUxmVpRanriH7Xw2NG3B \ No newline at end of file diff --git a/load_tester/run_load_tester_get_account_info.sh b/load_tester/run_load_tester_get_account_info.sh new file mode 100755 index 0000000..093ca8c --- /dev/null +++ b/load_tester/run_load_tester_get_account_info.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1 + +cargo run --bin load-tester http://localhost:10700 -d 10s -c 10 get-account-info --input-file ./fixture/accounts.txt \ No newline at end of file diff --git a/load_tester/src/cli.rs b/load_tester/src/cli.rs index b7729d8..62faed6 100644 --- a/load_tester/src/cli.rs +++ b/load_tester/src/cli.rs @@ -3,8 +3,8 @@ use reqwest::Url; #[derive(Parser, Clone)] pub struct LoadTesterCli { - /// Target URL - pub url: Url, + /// Target RPC server URL + pub rpc_url: Url, #[command(subcommand)] pub load_test_request: LoadTestRequestCommand, @@ -19,11 +19,11 @@ pub enum LoadTestRequestCommand { #[command(name = "get-account-info")] #[group(id = "get-account-info", required = true, multiple = false)] GetAccountInfoArgs { - /// the public key of the account + /// The public key of the account #[arg(short, long)] - pk: Option, + account_pk: Option, - /// the file to read the public keys from + /// The file to read the public keys from #[arg(short, long)] input_file: Option, }, diff --git a/load_tester/src/load_test_endpoint_params.rs b/load_tester/src/load_test_endpoint_params.rs index 04f4c67..e13d368 100644 --- a/load_tester/src/load_test_endpoint_params.rs +++ b/load_tester/src/load_test_endpoint_params.rs @@ -10,9 +10,12 @@ pub enum LoadTestEndpointParams { impl LoadTestEndpointParams { pub fn get_params_from_cli_args(cli_args: &LoadTesterCli) -> LoadTestEndpointParams { match &cli_args.load_test_request { - LoadTestRequestCommand::GetAccountInfoArgs { pk, input_file } => { - let pks = match (pk, input_file) { - (Some(pk), None) => vec![pk.clone()], + LoadTestRequestCommand::GetAccountInfoArgs { + account_pk, + input_file, + } => { + let account_pks = match (account_pk, input_file) { + (Some(account_pk), None) => vec![account_pk.clone()], (None, Some(input_file)) => read_to_string(input_file) .unwrap_or_else(|e| { panic!("failed to read file: file={}, error={}", input_file, e) @@ -23,7 +26,7 @@ impl LoadTestEndpointParams { .collect(), _ => unreachable!(), }; - LoadTestEndpointParams::GetAccountInfo(pks) + LoadTestEndpointParams::GetAccountInfo(account_pks) } } } diff --git a/load_tester/src/main.rs b/load_tester/src/main.rs index 833e8f8..e20c72c 100644 --- a/load_tester/src/main.rs +++ b/load_tester/src/main.rs @@ -3,7 +3,6 @@ use async_trait::async_trait; use clap::Parser; use cli::LoadTesterCli; use load_test_endpoint_params::LoadTestEndpointParams; -use log::debug; use rand::{rngs::StdRng, Rng, SeedableRng}; use requests::bench_get_account_info_request; use reqwest::Client; @@ -52,31 +51,29 @@ impl BenchSuite for LoadTester { } async fn bench(&mut self, state: &mut Self::WorkerState, _: &IterInfo) -> Result { - let t = Instant::now(); + let started_at = Instant::now(); - let response = match &self.load_test_endpoint_params { - LoadTestEndpointParams::GetAccountInfo(pks) => { - let index = state.prng.gen::() % pks.len(); - let pk = pks[index].as_str(); + let (response_status, response_length) = match &self.load_test_endpoint_params { + LoadTestEndpointParams::GetAccountInfo(account_pks) => { + let index = state.prng.gen::() % account_pks.len(); + let account_pk = account_pks[index].as_str(); - debug!("getAccountInfo request for pk={}", pk); - bench_get_account_info_request(&state.client, &self.cli_opts.url, pk).await? + bench_get_account_info_request(&state.client, &self.cli_opts.rpc_url, account_pk) + .await? } }; - let status = response.status().into(); - let bytes = response.bytes().await?.len() as u64; - let duration = t.elapsed(); + let duration = started_at.elapsed(); Ok(IterReport { duration, - status, - bytes, + status: response_status.into(), + bytes: response_length as u64, items: 1, }) } } -#[tokio::main] +#[tokio::main(flavor = "multi_thread")] async fn main() -> Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); diff --git a/load_tester/src/requests.rs b/load_tester/src/requests.rs index de2106f..2257053 100644 --- a/load_tester/src/requests.rs +++ b/load_tester/src/requests.rs @@ -1,27 +1,92 @@ -use anyhow::{Error, Result}; -use reqwest::{Client, Response, Url}; +use anyhow::{bail, Context, Result}; +use base64::{prelude::BASE64_STANDARD as base64, Engine}; +use log::debug; +use reqwest::{Client, StatusCode, Url}; use serde_json::json; +use solana_account_decoder::{UiAccount, UiAccountData, UiAccountEncoding}; +use solana_rpc_client_api::response::Response as RpcResponse; +use spl_token_2022::{solana_program::program_pack::Pack, state::Account}; pub async fn bench_get_account_info_request( client: &Client, url: &Url, - pubkey: &str, -) -> Result { - let req_body = json!({ + account_pk: &str, +) -> Result<(StatusCode, usize)> { + debug!("getAccountInfo request for account_pk={}", account_pk); + + let req_body: serde_json::Value = json!({ "jsonrpc": "2.0", "id": 1, "method": "getAccountInfo", "params": [ - pubkey, + account_pk, { "encoding": "base64" } ] }); - client - .post(url.clone()) - .json(&req_body) - .send() - .await - .map_err(Error::new) + let response = client.post(url.clone()).json(&req_body).send().await?; + let response_status = response.status(); + let response_bytes = response.bytes().await?; + let response_length = response_bytes.len(); + + if let StatusCode::OK = response_status { + debug!("response OK for account_pk={}", account_pk); + + let response_json: serde_json::Value = serde_json::from_slice(&response_bytes) + .with_context(|| { + format!( + "Couldn't parse response bytes to JSON for account_pk={}", + account_pk + ) + })?; + let result_value = response_json + .get("result") + .with_context(|| { + format!( + "Response object property 'result' is nullish for account_pk={}", + account_pk + ) + })? + .clone(); + let get_account_info_response: RpcResponse> = + serde_json::from_value(result_value).with_context(|| { + format!( + "Couldn't parse result value to RpcResponse for account_pk={}", + account_pk + ) + })?; + let account_data = get_account_info_response + .value + .with_context(|| format!("'value' property is nullish for account_pk={}", account_pk))? + .data; + match account_data { + UiAccountData::Binary(data, UiAccountEncoding::Base64) => { + let decoded_data = base64.decode(data).with_context(|| { + format!("Couldn't decode base64 data for pubkey {}", account_pk) + })?; + Account::unpack(&decoded_data) + .expect("returned account should be a valid token account"); + debug!( + "successfully deserialized account data for account_pk={}", + account_pk + ); + } + UiAccountData::Binary(_data, encoding) => { + bail!( + "Unexpected account data encoding for pubkey: encoding={:?}, pubkey={}", + encoding, + account_pk + ); + } + UiAccountData::Json(_) => { + bail!( + "Unexpected account data encoding for pubkey: encoding=Json, pubkey={}", + account_pk + ); + } + _ => unreachable!(), + } + } + Ok((response_status, response_length)) } From 7d26e203442fc5f10a095d139b082b96689bf7ae Mon Sep 17 00:00:00 2001 From: armedx86 <90354404+armedx86@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:31:32 +0100 Subject: [PATCH 3/6] Fix typo --- accounts_from_snapshot/examples/import_archive.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/accounts_from_snapshot/examples/import_archive.rs b/accounts_from_snapshot/examples/import_archive.rs index ad09810..2b5ba22 100644 --- a/accounts_from_snapshot/examples/import_archive.rs +++ b/accounts_from_snapshot/examples/import_archive.rs @@ -23,7 +23,7 @@ pub async fn main() { let (mut accounts_rx, _) = import_archive(PathBuf::from_str(&snapshot_archive_path).unwrap()).await; - let mut avg_batchsize_cummulator = 0; + let mut avg_batchsize_cumulator = 0; let mut loop_cnt = 0; let mut started_at = Instant::now(); let mut cnt_append_vecs: u32 = 0; @@ -43,7 +43,7 @@ pub async fn main() { } } trace!("batch size: {}", batch.len()); - avg_batchsize_cummulator += batch.len(); + avg_batchsize_cumulator += batch.len(); for item in batch.drain(..) { cnt_append_vecs += 1; @@ -62,6 +62,6 @@ pub async fn main() { // 35 info!( "Average batch size: {}", - avg_batchsize_cummulator as f64 / loop_cnt as f64 + avg_batchsize_cumulator as f64 / loop_cnt as f64 ); } From 957b8c6757fb4a234532a9bdc952ab768a2cc08d Mon Sep 17 00:00:00 2001 From: armedx86 <90354404+armedx86@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:34:59 +0100 Subject: [PATCH 4/6] Sort dependencies in Cargo.toml --- Cargo.toml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e358bf..e6e1057 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,9 @@ members = [ "accounts_from_snapshot", "accounts_on_demand", "common", - "token_account_storage", - "simulate_from_snapshot", "load_tester", + "simulate_from_snapshot", + "token_account_storage", ] [workspace.package] @@ -31,26 +31,26 @@ solana-runtime = "~2.0.16" solana-rpc-client-api = "~2.0.16" solana-download-utils = "~2.0.16" +anyhow = "1.0.70" +arrayref = "0.3.7" async-trait = "0.1.68" +base64 = "0.21.0" +bincode = "=1.3.3" +bitflags = "2.6.0" +borsh = "0.10.3" +bs58 = "0.4.0" dashmap = "5.4.0" -serde = { version = "1.0.160", features = ["derive"] } -serde_json = "1.0.96" +futures = "0.3.28" itertools = "0.10.5" -borsh = "0.10.3" -anyhow = "1.0.70" +lazy_static = "1.4.0" log = "0.4.17" -bincode = "=1.3.3" -bs58 = "0.4.0" lz4 = "1.24.0" -zstd = "0.11.2" prometheus = "0.13.3" -lazy_static = "1.4.0" -base64 = "0.21.0" -futures = "0.3.28" -tracing-subscriber = "0.3.16" -bitflags = "2.6.0" -arrayref = "0.3.7" +serde = { version = "1.0.160", features = ["derive"] } +serde_json = "1.0.96" tempfile = "3.2.0" +tracing-subscriber = "0.3.16" +zstd = "0.11.2" spl-token = "6.0.0" spl-token-2022 = "5.0.2" From dbc3d8617af4534a3ee400983c993378866a33cd Mon Sep 17 00:00:00 2001 From: armedx86 <90354404+armedx86@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:25:48 +0100 Subject: [PATCH 5/6] Replace more unwraps with context errors --- load_tester/src/requests.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/load_tester/src/requests.rs b/load_tester/src/requests.rs index 2257053..d20dc5a 100644 --- a/load_tester/src/requests.rs +++ b/load_tester/src/requests.rs @@ -65,8 +65,12 @@ pub async fn bench_get_account_info_request( let decoded_data = base64.decode(data).with_context(|| { format!("Couldn't decode base64 data for pubkey {}", account_pk) })?; - Account::unpack(&decoded_data) - .expect("returned account should be a valid token account"); + Account::unpack(&decoded_data).with_context(|| { + format!( + "Couldn't unpack token account data for account_pk={}", + account_pk + ) + })?; debug!( "successfully deserialized account data for account_pk={}", account_pk @@ -74,14 +78,14 @@ pub async fn bench_get_account_info_request( } UiAccountData::Binary(_data, encoding) => { bail!( - "Unexpected account data encoding for pubkey: encoding={:?}, pubkey={}", + "Unexpected binary account data encoding for pubkey: encoding={:?}, account_pk={}", encoding, account_pk ); } UiAccountData::Json(_) => { bail!( - "Unexpected account data encoding for pubkey: encoding=Json, pubkey={}", + "Unexpected account data encoding for pubkey: encoding=Json, account_pk={}", account_pk ); } From 30ab5994932c33997696cd99c0d67f73c48db94f Mon Sep 17 00:00:00 2001 From: armedx86 <90354404+armedx86@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:51:35 +0100 Subject: [PATCH 6/6] Fixes after review --- load_tester/src/main.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/load_tester/src/main.rs b/load_tester/src/main.rs index e20c72c..229420f 100644 --- a/load_tester/src/main.rs +++ b/load_tester/src/main.rs @@ -56,10 +56,13 @@ impl BenchSuite for LoadTester { let (response_status, response_length) = match &self.load_test_endpoint_params { LoadTestEndpointParams::GetAccountInfo(account_pks) => { let index = state.prng.gen::() % account_pks.len(); - let account_pk = account_pks[index].as_str(); - bench_get_account_info_request(&state.client, &self.cli_opts.rpc_url, account_pk) - .await? + bench_get_account_info_request( + &state.client, + &self.cli_opts.rpc_url, + &account_pks[index], + ) + .await? } };