diff --git a/Cargo.lock b/Cargo.lock index 5d862c3..ca870ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,29 +19,30 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.3.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ + "crypto-common", "generic-array", ] [[package]] name = "aes" -version = "0.6.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ - "aes-soft", - "aesni", + "cfg-if", "cipher", + "cpufeatures", ] [[package]] name = "aes-gcm" -version = "0.8.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ "aead", "aes", @@ -51,26 +52,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher", - "opaque-debug", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -88,9 +69,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "bff2cf94a3dbe2d57cbd56485e1bd7436455058034d6c2d47be51d4e5e4bc6ab" dependencies = [ "anstyle", "anstyle-parse", @@ -126,9 +107,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796" dependencies = [ "anstyle", "windows-sys", @@ -166,15 +147,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.13.1" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] -name = "base64" -version = "0.21.2" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bincode" @@ -197,15 +178,6 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -247,34 +219,34 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.27" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56b4c72906975ca04becb8a30e102dfecddd0c06181e3e95ddc444be28881f8" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", "windows-targets", ] [[package]] name = "cipher" -version = "0.2.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] name = "clap" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", @@ -282,9 +254,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", @@ -294,9 +266,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", @@ -306,9 +278,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cloudabi" @@ -335,6 +307,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + [[package]] name = "core-foundation" version = "0.9.3" @@ -361,10 +339,13 @@ dependencies = [ ] [[package]] -name = "cpuid-bool" -version = "0.2.0" +name = "crc32fast" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] [[package]] name = "crc64" @@ -372,6 +353,28 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2707e3afba5e19b75d582d88bc79237418f2a2a2d673d01cf9b03633b46e98f3" +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg 1.1.0", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -379,44 +382,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] [[package]] name = "ctr" -version = "0.6.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] [[package]] name = "curve25519-dalek" -version = "3.2.1" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rand_core 0.6.4", + "rustc_version", + "serde", "subtle", "zeroize", ] [[package]] -name = "curve25519-dalek-ng" -version = "3.0.3" +name = "curve25519-dalek-derive" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8dfd4d479156d9ad3fe6d1562f78ff31a9ba8831d3575126061541c7294e48" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "byteorder", - "digest 0.9.0", - "packed_simd_2", - "rand_core 0.5.1", - "serde", - "subtle-ng", - "zeroize", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -455,21 +461,23 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.3.8" +name = "der" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ - "serde", + "const-oid", + "zeroize", ] [[package]] -name = "digest" -version = "0.9.0" +name = "deranged" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ - "generic-array", + "powerfmt", + "serde", ] [[package]] @@ -478,31 +486,37 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "crypto-common", ] [[package]] -name = "ed25519" -version = "1.5.3" +name = "dtoa" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "ed25519" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ + "pkcs8", "serde", "signature", ] [[package]] name = "ed25519-dalek" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ "curve25519-dalek", "ed25519", - "rand 0.7.3", + "rand_core 0.6.4", "serde", - "serde_bytes", "sha2", "zeroize", ] @@ -549,6 +563,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "fiat-crypto" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" + [[package]] name = "fnv" version = "1.0.7" @@ -579,6 +599,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -587,9 +617,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -602,9 +632,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -612,15 +642,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -629,15 +659,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", @@ -646,21 +676,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -674,6 +704,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" @@ -704,15 +743,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] name = "ghash" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug", "polyval", @@ -911,6 +952,24 @@ dependencies = [ "serde", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipnet" version = "2.8.0" @@ -925,9 +984,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -958,15 +1017,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linux-raw-sys" @@ -994,7 +1047,7 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" name = "lox-distributor" version = "0.1.0" dependencies = [ - "base64 0.13.1", + "base64", "chrono", "clap", "futures", @@ -1003,12 +1056,16 @@ dependencies = [ "julianday", "lox-library", "lox_utils", + "prometheus", + "prometheus-client", "rand 0.8.5", "rdsys_backend", + "reqwest", "serde", "serde_json", "serde_with", - "time 0.3.28", + "sled", + "time", "tokio", "zkp", ] @@ -1018,21 +1075,22 @@ name = "lox-library" version = "0.1.0" dependencies = [ "aes-gcm", - "base64 0.13.1", + "base64", "bincode", "chrono", - "curve25519-dalek-ng", + "curve25519-dalek", "ed25519-dalek", "hex_fmt", "lazy_static", - "rand 0.7.3", + "prometheus", + "rand 0.8.5", "serde", "serde_with", "sha2", "statistical", "subtle", "thiserror", - "time 0.3.28", + "time", "zkp", ] @@ -1042,6 +1100,7 @@ version = "0.1.0" dependencies = [ "chrono", "console_error_panic_hook", + "getrandom 0.2.10", "js-sys", "julianday", "lazy_static", @@ -1049,7 +1108,7 @@ dependencies = [ "lox_utils", "rand 0.7.3", "serde_json", - "time 0.3.28", + "time", "wasm-bindgen", "zkp", ] @@ -1071,14 +1130,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "merlin" -version = "2.0.1" +name = "memoffset" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", "keccak", - "rand_core 0.5.1", + "rand_core 0.6.4", "zeroize", ] @@ -1279,13 +1347,14 @@ dependencies = [ ] [[package]] -name = "packed_simd_2" -version = "0.3.8" +name = "parking_lot" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ - "cfg-if", - "libm", + "instant", + "lock_api", + "parking_lot_core 0.8.6", ] [[package]] @@ -1295,7 +1364,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.8", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -1306,7 +1389,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", "windows-targets", ] @@ -1319,9 +1402,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1329,6 +1412,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -1336,16 +1429,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] -name = "polyval" -version = "0.4.5" +name = "platforms" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ - "cpuid-bool", + "cfg-if", + "cpufeatures", "opaque-debug", "universal-hash", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1361,6 +1467,50 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.1", + "protobuf", + "thiserror", +] + +[[package]] +name = "prometheus-client" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c4f1c9d81d556458f94c98f857748130ea9737bbd6053da497503b26ea63c" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.1", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "quote" version = "1.0.32" @@ -1575,6 +1725,15 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1590,7 +1749,7 @@ version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.2", + "base64", "bytes", "encoding_rs", "futures-core", @@ -1629,6 +1788,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.4" @@ -1687,28 +1855,25 @@ dependencies = [ ] [[package]] -name = "serde" -version = "1.0.188" +name = "semver" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] -[[package]] -name = "serde_bytes" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -1717,9 +1882,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -1740,11 +1905,11 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ - "base64 0.21.2", + "base64", "chrono", "hex", "indexmap 1.9.3", @@ -1752,14 +1917,14 @@ dependencies = [ "serde", "serde_json", "serde_with_macros", - "time 0.3.28", + "time", ] [[package]] name = "serde_with_macros" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" dependencies = [ "darling", "proc-macro2", @@ -1769,26 +1934,24 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "digest", ] [[package]] @@ -1802,9 +1965,9 @@ dependencies = [ [[package]] name = "signature" -version = "1.6.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" [[package]] name = "slab" @@ -1815,6 +1978,22 @@ dependencies = [ "autocfg 1.1.0", ] +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot 0.11.2", +] + [[package]] name = "smallvec" version = "1.11.0" @@ -1831,6 +2010,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "statistical" version = "1.0.0" @@ -1849,21 +2038,15 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle-ng" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.29" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -1878,25 +2061,25 @@ checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", @@ -1905,23 +2088,13 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -1929,15 +2102,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -1969,7 +2142,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2011,9 +2184,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -2084,11 +2257,11 @@ dependencies = [ [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "generic-array", + "crypto-common", "subtle", ] @@ -2136,12 +2309,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2150,9 +2317,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2160,9 +2327,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", @@ -2187,9 +2354,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2197,9 +2364,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", @@ -2210,9 +2377,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasm-streams" @@ -2345,9 +2512,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.3.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] @@ -2366,12 +2533,11 @@ dependencies = [ [[package]] name = "zkp" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf6dceea8522c0ade5abe68c1ca747e80b88adce2cd6a070e95f102037f331f" +source = "git+https://gitlab.torproject.org/onyinyang/lox-zkp#7dc7562ecdac3a6c7c7ecc31cace192174d34778" dependencies = [ - "curve25519-dalek-ng", + "curve25519-dalek", "merlin", - "rand 0.7.3", + "rand 0.8.5", "serde", "serde_derive", "thiserror", diff --git a/crates/lox-distributor/Cargo.toml b/crates/lox-distributor/Cargo.toml index 52ce60a..6a1e955 100644 --- a/crates/lox-distributor/Cargo.toml +++ b/crates/lox-distributor/Cargo.toml @@ -12,23 +12,26 @@ keywords = ["tor", "lox", "bridges"] [dependencies] julianday = "1.2.0" -base64 = "0.13.1" +base64 = "0.21.5" hyper = { version = "0.14.27", features = ["server"] } hex_fmt = "0.3" -futures = "0.3.28" -time = "0.3.28" +futures = "0.3.29" +time = "0.3.30" tokio = { version = "1", features = ["full", "macros", "signal"] } rand = "0.8.5" +reqwest = { version = "0.11", features = ["json", "stream"]} serde = { version = "1.0", features = ["derive", "rc"] } -serde_with = "3.3.0" -zkp = "0.8.0" - +serde_with = "3.4.0" +zkp = { git = "https://gitlab.torproject.org/onyinyang/lox-zkp" } lox-library = { path = "../lox-library", version = "0.1.0"} lox_utils = { path = "../lox-utils", version = "0.1.0"} rdsys_backend = { path = "../rdsys-backend-api", version = "0.2"} -clap = { version = "4.4.2", features = ["derive"] } -serde_json = "1.0.105" +clap = { version = "4.4.7", features = ["derive"] } +serde_json = "1.0.108" +prometheus = "0.13.3" +sled = "0.34.7" +prometheus-client = "0.22.0" [dependencies.chrono] -version = "0.4.27" +version = "0.4.31" features = ["serde"] diff --git a/crates/lox-distributor/README.md b/crates/lox-distributor/README.md index cbfe60f..329e769 100644 --- a/crates/lox-distributor/README.md +++ b/crates/lox-distributor/README.md @@ -1,12 +1,11 @@ # Lox Distributor The Lox distributor receives resources from [rdsys](https://gitlab.torproject.org/tpo/anti-censorship/rdsys) and writes them to [Lox -BridgeLines](https://git-crysp.uwaterloo.ca/iang/lox/src/master/src/bridge_table.rs#L42). Concurrently, it receives and responds to requests from [Lox clients](https://gitlab.torproject.org/tpo/anti-censorship/lox/lox-wasm). - +BridgeLines](https://git-crysp.uwaterloo.ca/iang/lox/src/master/src/bridge_table.rs#L42). Concurrently, it receives and responds to requests from [Lox clients](https://gitlab.torproject.org/tpo/anti-censorship/lox/lox-wasm). It saves the [LoxContext](https://gitlab.torproject.org/tpo/anti-censorship/lox-rs/-/blob/main/crates/lox-distributor/src/lox_context.rs) to a database every time the Lox bridgetable is updated and before the distributor is shutdown. ## Configure rdsys stream A test `config.json` is included for testing on a local instance of rdsys. This -can be edited to correspond to the desired types of resources and endpoints. +can be edited to correspond to the desired types of resources, endpoints and database configuration. ## Test Run @@ -26,6 +25,13 @@ Finally run rdsys: ./backend --config config.json ``` +## Database Config + +The database has a few configuration options. The path for where the database +should be read/written can be specified in the `config.json`. Rolling back to a +previous version of the database is also possible by passing the +`roll_back_date` flag at runtime and providing the date/time as a `%Y-%m-%d_%H:%M:%S` string. This argument should be passed if the `LoxContext` should be rolled back to a previous state due to, for example, a mass blocking event that is likely not due to Lox user behaviour. If the exact roll back date/time is not known, the last db entry within 24 hours from the passed `roll_back_date` will be used or else the program will fail gracefully. + ### Run Lox Distributor locally Simply run `cargo run -- config.json` :) diff --git a/crates/lox-distributor/config.json b/crates/lox-distributor/config.json index 177e5a2..438cab1 100644 --- a/crates/lox-distributor/config.json +++ b/crates/lox-distributor/config.json @@ -1,9 +1,19 @@ { - "endpoint": "http://127.0.0.1:7100/resource-stream", - "name": "https", - "token": "HttpsApiTokenPlaceholder", - "types": [ - "obfs2", - "scramblesuit" - ] + "db": { + "db_path": "lox_db" + + }, + "metrics_port": 5222, + "bridge_config": { + "percent_spares": 50 + }, + "rtype": { + "endpoint": "http://127.0.0.1:7100/resources", + "name": "https", + "token": "HttpsApiTokenPlaceholder", + "types": [ + "obfs2", + "scramblesuit" + ] + } } \ No newline at end of file diff --git a/crates/lox-distributor/src/db_handler.rs b/crates/lox-distributor/src/db_handler.rs new file mode 100644 index 0000000..acf0cfe --- /dev/null +++ b/crates/lox-distributor/src/db_handler.rs @@ -0,0 +1,150 @@ +use std::sync::{Arc, Mutex}; + +use crate::metrics::Metrics; +use crate::{lox_context, DbConfig}; +use chrono::{naive::Days, DateTime, Local, NaiveDateTime, Utc}; +use lox_library::{BridgeAuth, BridgeDb}; +use sled::IVec; + +pub struct DB { + db: sled::Db, +} + +impl DB { + pub fn write_context(&mut self, context: lox_context::LoxServerContext) { + let date = Local::now().format("context_%Y-%m-%d_%H:%M:%S").to_string(); + let json_result = serde_json::to_vec(&context).unwrap(); + println!("Writing context to the db with key: {:?}", date); + let _new_ivec = self.db.insert( + IVec::from(date.as_bytes().to_vec()), + IVec::from(json_result.clone()), + ); + assert_eq!( + self.db + .get(IVec::from(date.as_bytes().to_vec())) + .unwrap() + .unwrap(), + IVec::from(json_result) + ); + } + + pub fn open_new_or_existing_db( + db_config: DbConfig, + roll_back_date: Option, + metrics: Metrics, + ) -> Result<(DB, lox_context::LoxServerContext), sled::Error> { + let mut context: lox_context::LoxServerContext; + let (lox_db, context) = match sled::open(db_config.db_path) { + Ok(lox_db) => { + // Check if the lox_db already exists + if lox_db.was_recovered() { + context = read_lox_context_from_db(lox_db.clone(), roll_back_date); + context.metrics = metrics; + //Otherwise, create a new Lox context + } else { + let new_db = BridgeDb::new(); + let new_ba = BridgeAuth::new(new_db.pubkey); + context = lox_context::LoxServerContext { + db: Arc::new(Mutex::new(new_db)), + ba: Arc::new(Mutex::new(new_ba)), + extra_bridges: Arc::new(Mutex::new(Vec::new())), + to_be_replaced_bridges: Arc::new(Mutex::new(Vec::new())), + metrics, + }; + } + (DB { db: lox_db }, context) + } + Err(e) => { + panic!("Unable to read or create lox database! {:?}", e); + } + }; + Ok((lox_db, context)) + } +} + +fn read_lox_context_from_db( + lox_db: sled::Db, + roll_back_date: Option, +) -> lox_context::LoxServerContext { + let context: lox_context::LoxServerContext; + // Check if there is a roll back date and try to choose the appropriate context + // to rollback to, otherwise, take the last saved context + match roll_back_date { + // If roll back date has been specified, either the exact date or range should be set + Some(roll_back_date) => { + // If the date is specified and it's in the database, use that to populate the context + if lox_db.contains_key(roll_back_date.clone()).unwrap() { + // Find date/time in db and use the context from that date. + let ivec_context = lox_db + .get(IVec::from(roll_back_date.as_bytes().to_vec())) + .unwrap() + .unwrap(); + context = serde_json::from_slice(&ivec_context).unwrap(); + println!("Successfully used exact key {:?}", roll_back_date); + } else { + // If the exact date is not found, compute the start of the range as 24 hours prior to the specified date + match compute_startdate_string(roll_back_date.clone()){ + Some(start_date) => { + let start_date = start_date.format("context_%Y-%m-%d_%H:%M:%S").to_string(); + let r: sled::Iter = lox_db.range(IVec::from(start_date.as_bytes().to_vec())..IVec::from(roll_back_date.as_bytes().to_vec()), ); + match r.last(){ + Some(entry) => { + let ivec_context = entry.unwrap(); + let key: String = String::from_utf8(ivec_context.0.to_vec()).unwrap(); + println!("Successfully used range and key {:?}", key); + context = serde_json::from_slice(&ivec_context.1).unwrap(); + }, + //If no entries are found within the 24 hours prior to the specified date, Exit + None => panic!("UNEXPECTED DATE: No entries found in the 24 hours prior to the input roll_back_date"), + } + } + None => panic!("UNEXPECTED DATE: Tried to use input date to calculate 24 hour range but failed. Exiting."), + } + } + } + // Use the last entry to populate the Lox context if no rollback date is set (which should be most common) + None => { + context = use_last_context(lox_db); + } + } + context +} + +fn compute_startdate_string(date_range_end: String) -> Option> { + let parsed_end = + NaiveDateTime::parse_from_str(&date_range_end, "context_%Y-%m-%d_%H:%M:%S").unwrap(); + let dt = DateTime::::from_naive_utc_and_offset(parsed_end, Utc); + dt.with_timezone(&Utc).checked_sub_days(Days::new(1)) +} + +fn use_last_context(lox_db: sled::Db) -> lox_context::LoxServerContext { + let ivec_context = lox_db.last().unwrap().unwrap(); + let ivec_date: String = String::from_utf8(ivec_context.0.to_vec()).unwrap(); + println!("Using last context with date: {:?}", ivec_date); + serde_json::from_slice(&ivec_context.1).unwrap() +} + +#[cfg(test)] +mod tests { + use super::lox_context::LoxServerContext; + use super::DbConfig; + use super::Metrics; + use super::DB; + + #[test] + fn test_write_context() { + let (mut lox_db, _context) = + DB::open_new_or_existing_db(DbConfig::default(), None, Metrics::default()).unwrap(); + assert!( + lox_db.db.is_empty(), + "db read from context that shouldn't exist" + ); + let test_string = "{\"db\":{\"keypair\":[121,4,160,52,248,243,39,240,71,101,82,207,64,22,142,74,246,212,153,208,24,125,84,1,155,176,83,205,46,185,185,44],\"pubkey\":[160,195,123,233,25,157,126,89,37,85,193,202,132,49,53,205,169,199,176,14,251,234,40,100,156,84,155,125,186,244,134,220],\"openinv_buckets\":[5,4,7,3,8],\"distributed_buckets\":[9],\"current_k\":2,\"daily_bridges_distributed\":1},\"ba\":{\"lox_priv\":{\"x0tilde\":[143,9,251,99,139,233,249,99,12,151,209,199,61,9,226,112,162,129,137,136,195,3,66,179,223,161,50,85,30,157,37,0],\"x\":[[243,220,21,212,62,251,120,189,78,69,185,186,29,7,23,200,129,129,142,47,138,112,151,4,126,40,138,144,220,88,86,8],[79,133,82,56,114,75,230,209,153,126,84,177,85,238,191,235,7,187,196,196,228,60,103,178,44,88,5,143,238,120,72,12],[114,190,197,197,92,27,104,48,174,133,173,50,91,173,81,40,202,254,102,100,238,9,180,244,14,47,71,72,202,169,223,0],[109,226,189,23,245,119,6,254,111,241,15,1,66,129,65,190,153,106,160,142,102,92,76,111,147,235,77,118,198,131,78,8],[219,73,214,47,104,208,140,251,118,150,220,157,85,115,83,86,89,232,171,242,152,64,20,61,209,119,78,245,36,204,85,11],[126,164,101,171,7,207,113,121,133,92,210,120,97,92,70,89,142,29,89,58,9,83,90,126,153,202,216,250,123,157,30,5],[25,203,47,124,192,229,50,210,39,186,172,157,71,221,90,252,83,0,234,180,15,228,133,110,58,222,51,147,110,190,237,5]]},\"lox_pub\":{\"X\":[[52,135,83,91,17,4,67,24,173,18,214,24,58,154,63,119,67,136,188,179,157,120,218,195,200,188,209,141,93,24,124,94],[238,46,17,216,198,220,35,70,61,27,186,253,177,7,221,231,169,81,99,102,18,128,125,114,184,15,246,233,1,244,118,61],[202,55,212,100,56,227,184,106,93,175,150,254,8,19,166,25,194,60,224,172,26,76,98,122,17,253,72,94,54,207,109,21],[216,173,4,2,65,255,161,45,106,111,132,219,209,220,33,26,43,173,120,117,116,30,45,151,244,206,240,255,231,179,175,103],[150,200,66,157,230,92,163,117,225,136,193,109,1,35,120,151,48,182,82,35,7,93,239,212,39,67,11,15,239,235,234,13],[172,116,53,192,167,34,113,160,85,68,184,65,117,63,29,26,138,132,224,234,105,96,194,93,220,240,88,237,110,113,86,82],[18,225,205,220,116,77,165,132,231,244,189,5,159,221,110,177,187,104,168,78,255,134,92,17,109,161,77,58,72,192,205,114]]},\"migration_priv\":{\"x0tilde\":[211,143,194,8,37,8,12,55,39,248,78,75,118,168,145,205,240,248,252,53,185,227,9,219,69,45,75,103,105,146,187,7],\"x\":[[104,107,78,46,137,189,128,168,148,77,184,118,199,234,137,81,190,103,120,180,134,231,215,197,20,176,51,138,238,54,110,12],[31,250,96,78,75,51,84,243,27,66,21,169,30,15,13,96,37,74,133,201,120,159,98,129,42,239,221,169,67,134,143,8],[74,167,255,153,36,234,214,227,250,25,106,172,36,25,72,176,81,222,197,223,237,86,17,230,97,249,107,70,91,21,190,15],[23,236,100,248,42,74,41,2,155,96,226,199,117,227,182,36,96,99,177,192,222,251,105,188,196,163,43,80,49,30,22,10],[242,31,173,104,138,50,162,156,255,85,58,180,93,228,130,239,115,14,89,179,114,236,140,190,116,16,6,147,121,27,92,11]]},\"migration_pub\":{\"X\":[[66,150,237,69,9,213,22,190,254,2,95,43,154,115,131,141,71,35,197,202,154,163,159,164,64,16,248,83,214,60,227,23],[26,26,30,134,61,140,209,165,221,146,165,203,213,57,60,16,82,185,177,218,28,59,86,37,139,88,118,69,43,120,49,97],[142,238,183,118,107,13,48,154,94,204,119,104,218,179,149,208,242,151,228,117,6,125,138,65,92,160,91,166,62,75,224,127],[6,134,209,57,107,68,65,24,78,50,197,79,39,74,239,181,77,57,17,173,30,21,231,241,211,185,78,235,219,138,235,49],[88,146,145,147,186,255,230,241,145,139,149,123,247,101,222,173,86,23,22,48,230,0,148,222,205,143,17,5,5,63,199,55]]},\"migrationkey_priv\":{\"x0tilde\":[108,193,14,7,211,244,9,190,251,186,173,191,159,67,226,168,60,109,225,13,157,225,5,88,42,21,40,103,143,79,232,0],\"x\":[[208,84,202,133,58,81,191,86,34,124,80,151,105,145,135,13,250,255,151,217,162,212,204,212,244,215,202,138,14,93,6,11],[253,152,102,66,182,93,38,129,126,173,154,32,46,2,96,174,197,211,100,130,19,206,64,153,40,104,51,36,99,163,154,10],[32,158,137,175,136,206,72,252,69,225,103,80,1,175,238,240,204,210,185,1,85,206,146,234,252,201,193,19,150,76,254,9]]},\"migrationkey_pub\":{\"X\":[[158,136,224,166,235,252,254,136,145,186,93,50,217,48,51,242,90,47,99,179,211,206,102,222,15,205,61,20,178,224,11,85],[238,183,52,40,190,24,231,89,149,158,183,90,255,178,156,37,47,223,162,251,128,222,16,185,125,91,29,185,76,121,209,15],[62,76,165,228,36,162,30,208,29,173,174,40,129,38,182,84,196,210,67,125,115,64,85,197,118,114,39,52,111,82,125,13]]},\"reachability_priv\":{\"x0tilde\":[36,187,28,100,96,130,72,135,158,51,170,109,52,72,103,90,220,74,212,104,103,92,247,69,35,235,11,53,107,234,77,0],\"x\":[[199,24,219,64,123,139,129,139,190,139,114,172,254,52,193,46,65,66,87,240,7,10,139,158,232,40,99,230,210,179,71,5],[186,173,85,159,209,12,113,8,136,170,194,128,243,174,20,78,188,57,63,144,163,218,59,102,107,184,152,42,149,22,66,6],[110,162,248,148,212,200,180,133,86,76,209,152,185,64,46,128,131,95,13,130,190,151,83,24,102,96,201,96,179,133,48,3]]},\"reachability_pub\":{\"X\":[[70,56,136,60,123,40,11,124,4,170,204,160,93,221,48,117,145,66,146,206,162,54,24,98,189,115,191,102,252,51,83,110],[60,52,219,76,2,245,192,204,150,195,154,211,150,104,201,53,43,137,32,135,133,205,82,109,50,186,87,52,27,254,160,46],[168,172,24,38,194,113,8,1,124,179,17,190,244,114,228,122,53,24,62,138,27,177,89,238,167,79,64,104,39,10,216,94]]},\"invitation_priv\":{\"x0tilde\":[98,218,42,226,106,65,201,104,75,104,13,80,9,213,177,167,186,16,188,139,208,56,23,22,16,17,206,187,170,253,122,15],\"x\":[[112,9,221,148,24,75,246,241,128,87,120,9,146,203,160,238,250,10,215,116,25,138,172,150,74,244,19,67,248,116,234,5],[246,214,228,161,154,83,161,139,113,170,189,7,6,84,104,101,167,201,174,249,29,219,155,108,165,19,88,252,32,169,121,14],[115,155,88,53,123,252,230,51,173,10,205,187,83,108,129,241,144,178,210,189,22,197,204,174,164,143,125,101,82,139,18,1],[235,162,227,81,108,197,23,213,19,74,249,242,84,12,11,134,244,102,171,74,125,234,94,134,17,200,235,172,3,236,212,9],[178,57,128,236,178,248,50,5,214,239,97,153,20,55,197,220,217,98,4,118,246,131,124,90,113,121,169,129,211,175,216,1]]},\"invitation_pub\":{\"X\":[[18,91,111,4,165,234,85,12,151,195,5,213,91,191,167,191,89,60,218,90,204,99,35,166,37,73,105,134,227,169,158,27],[52,2,100,3,112,118,66,31,105,121,74,208,215,223,40,210,103,149,254,18,232,150,136,241,112,136,4,98,187,175,214,25],[156,112,201,126,204,98,91,120,114,189,170,32,238,249,248,163,196,109,205,150,163,209,49,95,193,113,191,226,255,83,0,86],[120,15,100,23,59,173,212,124,73,190,37,77,132,190,169,48,183,222,194,183,119,194,39,184,73,206,193,154,200,129,252,117],[78,246,199,66,152,244,85,179,40,77,215,77,81,206,248,236,102,112,244,74,247,180,207,195,109,70,95,242,207,58,179,109]]},\"bridgedb_pub\":[160,195,123,233,25,157,126,89,37,85,193,202,132,49,53,205,169,199,176,14,251,234,40,100,156,84,155,125,186,244,134,220],\"bridge_table\":{\"counter\":10,\"keys\":{\"4\":[98,77,88,28,245,60,12,162,231,142,175,244,134,36,108,189],\"5\":[231,37,133,52,229,253,115,13,11,167,181,65,108,249,202,39],\"8\":[104,79,30,48,46,140,65,219,127,227,243,161,132,163,136,8],\"10\":[156,165,205,136,12,139,204,191,14,8,129,174,126,95,179,80],\"3\":[151,150,241,156,53,136,84,201,6,109,247,5,174,211,73,255],\"1\":[111,79,32,173,35,142,62,131,8,17,215,22,0,36,146,65],\"6\":[40,76,205,122,89,219,86,59,189,63,21,108,107,149,34,120],\"9\":[138,27,80,18,27,19,245,215,20,19,26,158,17,7,77,194],\"7\":[158,57,44,39,78,80,130,81,160,117,234,188,89,51,62,134],\"2\":[108,105,146,211,217,191,102,209,15,44,87,22,53,35,34,243]},\"buckets\":{\"2\":[{\"addr\":[49,57,53,46,55,50,46,55,52,46,49,49,55,0,0,0],\"port\":28746,\"uid_fingerprint\":8013544061081454931,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,50,57,49,49,48,66,55,68,57,69,57,54,48,65,53,67,52,50,56,52,56,55,55,50,65,70,54,50,52,65,51,66,50,55,65,50,68,69,52,69,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[49,50,56,46,49,51,57,46,52,52,46,49,53,55,0,0],\"port\":10839,\"uid_fingerprint\":16072135124933872676,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,55,67,53,65,51,68,67,53,51,49,48,55,53,53,48,54,65,50,52,52,65,51,67,70,49,57,66,53,55,70,52,68,69,52,55,55,67,65,54,51,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[49,51,52,46,50,48,55,46,50,51,53,46,49,48,53,0],\"port\":9042,\"uid_fingerprint\":5838789323103363859,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,56,48,55,56,70,57,55,66,50,50,49,53,55,69,66,55,49,54,65,69,66,68,53,54,65,57,52,55,54,67,51,55,56,57,57,65,68,70,54,65,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"9\":[{\"addr\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\"port\":63174,\"uid_fingerprint\":9189545078772697644,\"info\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,69,68,67,51,65,65,48,51,70,69,54,69,53,50,66,69,55,55,56,68,68,52,54,70,50,55,68,51,53,56,57,57,51,66,55,65,49,48,65,52,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"7\":[{\"addr\":[49,57,49,46,49,56,48,46,49,48,51,46,50,51,54,0],\"port\":50309,\"uid_fingerprint\":17576062521251310994,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,70,68,56,68,67,55,69,70,57,50,70,49,70,49,52,68,48,48,67,70,57,68,54,70,54,50,57,55,65,51,52,54,56,66,53,57,69,55,48,55,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"6\":[{\"addr\":[49,57,49,46,49,56,48,46,49,48,51,46,50,51,54,0],\"port\":50309,\"uid_fingerprint\":17576062521251310994,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,70,68,56,68,67,55,69,70,57,50,70,49,70,49,52,68,48,48,67,70,57,68,54,70,54,50,57,55,65,51,52,54,56,66,53,57,69,55,48,55,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\"port\":63174,\"uid_fingerprint\":922068108643293997,\"info\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,70,68,56,68,67,55,69,70,57,50,70,49,70,49,52,68,48,48,67,70,57,68,54,70,54,50,57,55,65,51,52,54,56,66,53,57,69,55,48,55,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]},{\"addr\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\"port\":63174,\"uid_fingerprint\":9189545078772697644,\"info\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,69,68,67,51,65,65,48,51,70,69,54,69,53,50,66,69,55,55,56,68,68,52,54,70,50,55,68,51,53,56,57,57,51,66,55,65,49,48,65,52,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]}],\"4\":[{\"addr\":[49,50,56,46,49,51,57,46,52,52,46,49,53,55,0,0],\"port\":10839,\"uid_fingerprint\":16072135124933872676,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,55,67,53,65,51,68,67,53,51,49,48,55,53,53,48,54,65,50,52,52,65,51,67,70,49,57,66,53,55,70,52,68,69,52,55,55,67,65,54,51,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"5\":[{\"addr\":[49,51,52,46,50,48,55,46,50,51,53,46,49,48,53,0],\"port\":9042,\"uid_fingerprint\":5838789323103363859,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,56,48,55,56,70,57,55,66,50,50,49,53,55,69,66,55,49,54,65,69,66,68,53,54,65,57,52,55,54,67,51,55,56,57,57,65,68,70,54,65,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"10\":[{\"addr\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\"port\":63174,\"uid_fingerprint\":14821306939685929169,\"info\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,66,48,56,49,49,56,65,56,48,68,55,54,54,66,65,57,65,56,70,57,65,55,50,50,67,50,54,54,49,52,69,48,54,66,56,54,69,52,67,65,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]},{\"addr\":[57,55,46,49,55,53,46,57,49,46,55,0,0,0,0,0],\"port\":27719,\"uid_fingerprint\":9270304952963857555,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,69,68,67,51,65,65,48,51,70,69,54,69,53,50,66,69,55,55,56,68,68,52,54,70,50,55,68,51,53,56,57,57,51,66,55,65,49,48,65,52,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[50,50,46,50,52,49,46,56,49,46,54,50,0,0,0,0],\"port\":36457,\"uid_fingerprint\":14409951350199027285,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,68,68,56,56,57,53,48,56,48,48,50,70,57,50,53,67,70,55,52,69,49,51,48,68,49,48,49,67,68,70,49,67,49,51,51,65,65,65,68,49,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"3\":[{\"addr\":[49,57,53,46,55,50,46,55,52,46,49,49,55,0,0,0],\"port\":28746,\"uid_fingerprint\":8013544061081454931,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,50,57,49,49,48,66,55,68,57,69,57,54,48,65,53,67,52,50,56,52,56,55,55,50,65,70,54,50,52,65,51,66,50,55,65,50,68,69,52,69,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"8\":[{\"addr\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\"port\":63174,\"uid_fingerprint\":922068108643293997,\"info\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,70,68,56,68,67,55,69,70,57,50,70,49,70,49,52,68,48,48,67,70,57,68,54,70,54,50,57,55,65,51,52,54,56,66,53,57,69,55,48,55,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"port\":0,\"uid_fingerprint\":0,\"info\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],\"1\":[{\"addr\":[50,49,50,46,49,54,54,46,49,54,54,46,54,55,0,0],\"port\":32414,\"uid_fingerprint\":3643046579853129326,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,66,48,56,49,49,56,65,56,48,68,55,54,54,66,65,57,65,56,70,57,65,55,50,50,67,50,54,54,49,52,69,48,54,66,56,54,69,52,67,65,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[49,51,50,46,49,56,49,46,49,53,53,46,55,54,0,0],\"port\":55241,\"uid_fingerprint\":4215620865531854407,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,67,66,70,56,51,51,51,49,54,53,70,49,67,51,53,65,68,70,48,54,66,70,52,50,53,49,48,52,69,56,51,51,65,68,68,65,49,54,53,66,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{\"addr\":[49,53,54,46,55,50,46,50,49,57,46,52,57,0,0,0],\"port\":56030,\"uid_fingerprint\":8564141605929456761,\"info\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,68,56,66,67,67,48,69,67,70,69,50,67,57,54,53,65,66,67,50,57,67,54,50,55,70,68,67,69,67,48,66,48,54,67,53,65,68,66,49,54,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}]},\"encbuckets\":{\"5\":[212,104,142,74,232,164,167,215,241,173,77,228,245,132,164,225,11,40,120,21,101,201,141,172,165,1,169,6,103,29,201,195,239,64,154,123,117,201,31,58,194,198,58,27,147,125,3,99,188,202,0,92,144,102,139,27,250,91,206,233,232,25,130,91,199,92,186,150,33,0,168,1,133,229,208,96,104,225,211,42,136,126,37,209,110,149,128,66,145,196,77,148,55,38,221,115,205,184,149,69,34,137,39,109,135,58,85,10,134,181,86,223,200,32,90,195,75,203,25,123,211,183,22,232,122,50,105,151,39,29,185,135,37,164,228,102,249,177,158,27,186,128,59,3,28,120,156,8,222,247,100,213,144,61,31,57,253,72,62,202,216,137,226,217,46,136,135,169,151,216,101,95,210,43,158,172,11,38,45,166,91,240,116,102,84,198,129,255,84,114,82,235,162,163,25,206,227,66,88,161,170,255,46,43,243,216,126,253,116,246,136,201,13,127,108,121,249,188,119,223,9,62,216,38,152,45,119,159,174,229,255,52,36,97,11,112,137,43,147,85,104,160,128,205,8,135,236,211,78,137,237,9,130,196,17,5,235,7,116,184,46,161,0,138,234,15,120,169,128,30,143,37,161,161,7,8,245,130,192,173,239,38,171,77,88,139,227,41,162,87,111,165,218,3,102,194,231,208,167,181,252,199,82,79,76,215,82,49,114,93,80,144,111,96,153,145,128,161,173,148,223,151,43,198,82,255,170,10,89,43,203,137,200,70,113,150,115,209,185,128,76,134,81,163,104,231,134,113,9,89,206,155,162,216,44,145,238,160,112,216,162,143,189,38,56,218,188,224,17,249,174,169,174,62,35,56,110,10,189,85,43,232,96,177,88,23,151,73,31,15,107,158,186,21,112,249,236,58,111,126,11,159,138,120,22,213,83,9,129,181,148,5,180,210,82,203,248,182,254,98,212,175,154,155,20,232,34,158,127,223,76,253,105,145,132,104,180,205,59,188,223,213,86,25,151,241,176,167,116,164,14,196,79,17,192,162,76,189,57,48,103,196,114,248,32,61,77,144,14,200,134,213,13,190,103,179,90,96,174,43,217,229,187,64,193,128,134,41,207,112,41,235,144,5,219,43,147,41,191,138,89,208,30,81,184,2,31,166,121,159,211,242,80,164,3,227,146,11,248,172,198,32,177,89,23,115,55,167,172,127,75,93,185,213,115,70,134,226,26,117,53,238,187,3,147,43,20,142,56,148,19,170,219,107,128,0,117,149,172,40,236,186,250,102,242,208,154,101,159,110,162,64,113,203,113,5,209,79,91,180,75,99,37,13,10,207,41,199,239,51,132,237,216,35,30,143,232,229,252,152,92,149,76,36,12,241,113,110,177,98,226,147,173,90,21,68,106,218,85,242,27,22,213,79,115,24,191,247,80,94,223,193,77,70,25,204,57,204,81,25,39,116,32,162,60,192,239,134,162,233,186,13,84,200,111,142,150,67,229,246,48,119,129,113,39,200,254,104,97,194,62,108,103,220,120,221,138,5,4,191,89,164,144,243,175,75,119,127,100,90,121,140,33,223,21,204,141,79,168,218,102,58,169,107,108,23,215,228,237,77,162,142,232,29,224,192,74,227,1,208,26,170,156,183,134,69,132,159,128,249,105,215,72,138,87,181,164,96,243,3,130,94,90,89,122,196,27,237,132,203,91,29,64,67,120,98,139,181,62,252,39,67,120,34,84,55,38,74,218,170,113,115,255,140,30,207,8,244,250,111,82,243,19,107,80,40,120,209,103,239,36,88,72,151,89,172,12,194,184,42,126,110,49,90,66,251,222,70,224,216,9,211,104,254,193,212,169,241,27,64,198,147,80,100,106,46,143,171,131,34,91,116,231,10,14,73,113,12,255,71,243,232,123,30,226,203,172,3,31,148,171,37,210,251,17,0,87,244,206,176,64,39,80,125,67,33,24,253,38,250,4,156,147,175,132,4,7,208,169,251,137,214,139,212,150,43,75,39,98,180,173,96,150,198,54,39,217,229,171,206,139,57,239,1,210,155,164,18,114,207,57,103,63,95,85,77,201,1,155,190,3,51,177,75,145,250,110,106,87,65,28,210,40,73,155,176,82,252,230,239,51,87,195,5,173,37,54,234,106,201,212,43,202,177,19,217,109,179,210,12,1,222,251,167,213,6,244,80,224,239,67,124,123,253,220,73,194,165,191,61,225,86,142,123,103,231,248,181,117,137,92,221,50,214,78,112],\"7\":[133,67,45,40,43,226,44,44,55,133,123,44,38,130,99,59,186,45,165,90,12,117,245,92,142,58,66,87,125,251,210,214,132,228,64,30,149,114,212,247,217,223,241,174,38,63,95,97,164,250,225,169,70,152,184,254,247,24,51,143,240,246,213,237,161,101,70,47,173,234,57,79,56,160,210,81,230,73,42,198,255,109,176,54,211,15,124,40,125,88,249,72,193,51,98,248,246,153,137,171,157,197,57,252,163,83,132,122,127,52,210,116,83,240,126,17,71,77,244,249,25,213,183,86,245,158,214,98,101,230,95,96,74,4,166,90,141,77,200,78,108,132,153,162,133,59,177,106,38,158,223,133,207,51,219,198,241,192,244,167,162,176,1,13,191,181,235,103,40,227,183,60,15,77,122,154,120,94,249,23,53,128,232,62,31,84,106,178,105,36,143,66,254,120,219,128,206,58,207,20,95,134,80,3,176,130,67,215,42,2,170,43,141,151,133,83,5,254,187,96,102,111,181,195,71,25,163,203,227,33,44,197,5,205,224,125,205,240,192,93,178,115,16,117,249,186,229,144,174,60,60,197,137,144,77,154,63,169,198,144,71,251,238,9,62,179,140,187,203,58,112,214,26,216,235,41,112,173,131,108,12,185,168,93,35,96,110,127,108,205,190,237,61,173,93,187,165,214,166,157,197,20,191,156,5,217,246,115,216,76,112,85,107,243,111,153,255,148,229,16,162,82,60,86,2,199,64,149,23,23,124,196,193,186,75,114,204,30,197,82,253,237,141,204,246,136,235,255,29,250,203,194,32,207,202,14,26,149,193,230,35,89,8,170,107,45,77,223,66,76,248,76,156,127,149,126,58,224,209,21,43,133,135,140,67,231,101,86,177,78,252,184,153,36,107,239,171,38,236,101,172,82,144,155,81,248,18,60,190,94,101,171,39,11,191,246,152,149,164,26,80,165,58,254,136,11,221,226,159,148,15,217,173,176,82,221,182,99,187,169,94,80,177,66,63,220,234,25,138,27,208,193,3,165,219,14,42,12,249,86,72,132,201,18,139,226,93,248,209,232,106,15,178,164,3,50,226,140,171,86,101,206,220,55,239,255,211,43,41,82,166,168,43,101,170,228,87,55,156,164,28,131,107,33,23,174,156,132,219,129,128,137,33,21,193,205,64,28,195,242,173,38,191,193,181,64,13,163,112,104,28,50,142,153,54,70,170,8,114,69,144,2,208,152,30,158,167,15,161,94,87,148,178,186,205,16,139,158,174,111,148,51,64,210,190,88,226,206,99,26,226,247,147,84,181,29,75,125,217,238,55,191,39,148,43,41,47,143,198,38,128,173,59,102,13,15,89,59,43,158,201,200,15,56,51,38,109,165,77,40,167,119,122,87,96,191,40,130,186,79,251,161,10,227,69,19,46,235,51,104,7,253,217,1,32,59,187,210,147,92,204,109,82,226,202,71,120,196,146,31,85,230,55,99,157,42,68,217,209,112,149,19,225,34,72,197,187,147,195,122,187,43,105,60,36,134,112,59,127,171,55,189,27,87,240,32,92,134,138,95,162,47,214,197,157,103,49,249,173,13,25,233,75,200,162,4,180,15,44,15,210,101,199,47,197,40,67,22,78,232,230,92,2,113,209,177,199,105,217,5,133,57,164,182,8,43,113,151,253,159,92,96,212,208,201,69,139,224,97,38,28,204,46,249,111,157,74,127,42,159,159,194,175,246,142,70,252,226,236,99,98,110,131,25,254,60,207,31,204,51,24,28,164,157,3,202,100,215,12,49,33,121,202,160,185,71,204,188,187,144,93,10,212,104,80,93,6,11,217,164,157,86,36,250,111,226,207,234,43,58,1,229,42,178,37,155,144,1,179,188,38,50,121,239,241,124,80,117,191,115,100,30,196,58,66,39,2,138,220,251,12,83,37,3,108,22,115,232,75,25,99,168,19,190,27,22,79,18,187,190,138,93,237,41,28,199,99,30,125,169,230,49,2,169,9,245,141,232,152,171,26,189,152,146,165,230,134,238,21,195,92,60,115,139,148,71,32,48,186,236,155,164,234,107,211,204,77,76,59,231,151,75,230,156,84,171,151,146,126,181,9,212,152,123,25,26,87,29,250,55,204,121,221,237,16,58,160,219,54,12,223,36,147,157,94,192,45,185,13,32,214,112,155,242,245,62,46,96,29,236,16,2,54,28,229,9,35,7,11,110,251,181,81,44,136,35,75,93,50,168,15,64,217,236,246,156],\"9\":[32,9,165,173,194,126,208,224,139,145,74,38,105,240,179,139,109,250,199,118,91,133,178,129,211,139,235,127,167,187,206,12,57,15,143,58,198,104,64,245,15,93,27,93,17,60,3,188,121,220,93,161,67,112,235,60,25,72,86,253,141,79,214,233,96,26,98,108,108,208,205,34,16,114,169,239,4,19,1,151,57,204,20,182,113,161,187,22,65,224,94,179,23,122,218,224,162,17,111,73,122,64,111,9,124,238,189,223,64,109,205,150,105,193,213,190,173,22,27,139,191,31,46,190,212,86,101,104,13,13,94,21,153,35,232,171,223,141,119,85,185,142,118,137,166,143,129,249,54,48,66,111,90,152,147,54,120,196,58,22,74,234,81,36,44,95,163,67,76,124,44,149,141,19,159,77,186,53,25,161,15,117,10,137,64,182,127,73,20,175,197,77,116,113,148,135,73,204,124,121,141,38,46,228,20,188,253,190,165,40,183,126,210,82,198,55,50,225,84,168,187,97,6,56,77,246,195,211,84,38,86,241,165,244,11,244,252,152,238,154,184,165,77,185,32,122,68,70,167,245,211,247,189,75,220,93,240,199,245,139,16,218,5,157,87,180,67,70,223,201,5,91,102,161,111,197,22,201,237,121,111,72,138,237,80,249,205,149,249,202,183,42,26,33,7,115,166,103,152,97,182,68,200,203,95,238,20,113,206,96,203,23,78,58,68,136,84,57,118,155,82,101,161,146,8,81,126,77,85,187,82,20,181,183,91,103,19,249,77,7,150,4,242,6,45,46,23,210,34,32,228,215,68,78,191,174,37,129,36,102,219,187,154,90,124,147,186,191,241,116,76,137,96,151,243,192,90,97,13,88,170,114,252,80,41,238,145,113,205,167,131,161,251,16,111,132,215,215,31,197,130,88,3,152,75,50,111,148,104,45,211,69,206,216,80,167,69,109,30,37,94,33,50,233,51,191,65,48,169,89,127,142,123,79,84,176,17,150,187,30,114,214,204,220,180,180,61,33,156,244,121,247,66,209,185,99,66,97,39,241,31,29,189,230,85,29,67,10,22,207,158,153,252,106,220,195,188,6,95,9,88,179,222,213,167,91,175,105,74,139,242,101,216,226,206,228,255,204,213,168,110,211,147,189,150,180,76,240,114,2,157,197,207,227,250,193,107,206,114,85,207,169,16,93,125,94,245,29,232,86,29,207,254,105,88,24,158,188,225,139,213,77,166,27,179,202,96,122,107,227,32,124,183,199,19,5,199,132,223,113,136,199,90,24,178,15,184,83,212,222,186,77,102,116,226,119,104,80,220,82,17,133,183,134,79,148,206,128,252,248,160,80,201,31,154,39,139,154,249,215,8,61,102,200,151,8,102,127,153,166,217,46,254,41,41,109,7,191,127,117,211,1,209,193,132,211,252,97,174,97,29,78,218,22,141,92,54,99,90,100,16,230,230,252,251,96,17,119,150,58,50,18,234,242,101,47,245,119,178,33,233,188,80,253,117,248,34,195,183,66,166,94,240,26,213,32,0,0,132,167,219,212,83,167,96,97,253,233,140,117,223,236,124,47,164,100,95,124,222,252,27,10,203,185,241,188,111,22,147,97,247,200,229,17,56,161,235,99,60,171,238,236,1,231,43,226,72,58,94,240,186,234,1,224,44,166,121,56,233,0,172,64,139,126,51,250,188,182,23,48,155,213,54,168,91,178,125,70,57,42,196,40,51,35,251,105,7,226,216,230,159,87,196,74,26,179,116,200,43,99,207,153,225,36,78,33,168,57,2,100,107,32,209,158,144,22,147,145,255,226,2,233,46,181,84,208,170,189,73,153,229,171,48,50,69,58,94,46,245,201,145,18,18,41,186,79,145,14,129,112,9,244,118,142,250,123,197,45,38,51,21,199,224,60,248,207,247,108,43,177,93,209,128,128,196,214,191,55,179,113,75,184,23,226,168,30,22,214,86,47,53,29,160,178,95,96,141,114,217,76,43,143,204,135,170,240,85,163,12,149,6,16,134,241,174,80,96,230,94,19,89,93,248,120,206,125,11,14,63,199,10,198,149,82,25,95,124,185,85,78,115,121,1,177,96,72,125,90,179,151,169,195,104,118,229,245,85,182,248,126,2,231,51,210,88,86,162,186,208,213,124,24,45,85,215,236,99,69,255,220,230,27,83,210,148,212,22,49,1,95,187,46,212,230,169,15,41,108,188,146,245,92,86,114,176,152,100,126,193,21,126,129,76,158,158,168],\"4\":[240,57,83,70,115,206,170,187,199,104,38,218,103,81,178,153,109,1,21,229,238,191,25,26,253,133,215,31,198,66,136,177,103,113,149,189,224,55,214,1,237,103,24,219,103,224,183,195,173,27,248,10,202,132,53,177,47,81,141,123,10,21,60,134,234,165,138,172,246,29,164,59,182,12,144,9,186,4,3,78,101,142,133,90,155,148,232,89,177,66,55,36,12,15,195,200,232,237,165,169,171,136,179,0,120,31,25,56,195,112,194,86,82,54,67,243,7,187,195,91,13,130,239,186,240,77,29,180,176,133,207,9,133,88,238,201,73,253,200,179,59,158,95,111,165,44,72,102,114,183,222,9,194,215,0,210,128,218,147,69,161,218,42,193,46,193,155,62,189,28,107,81,37,136,43,194,73,40,161,94,44,204,253,228,157,34,92,8,249,133,110,14,58,96,219,211,82,84,50,224,35,151,250,157,17,49,43,56,82,239,0,82,241,201,115,92,145,91,72,152,239,70,30,118,236,223,61,66,3,226,5,175,245,152,129,113,117,159,71,122,190,12,2,161,158,190,212,121,138,88,155,169,97,119,107,43,85,227,7,244,225,199,75,98,191,228,101,31,167,216,147,100,229,100,119,119,33,134,133,201,16,57,96,68,89,26,152,229,145,27,25,135,182,224,126,214,59,231,109,239,72,53,19,83,194,240,252,166,93,39,205,190,236,127,59,55,159,126,5,220,73,200,140,5,242,207,137,90,97,109,233,72,68,230,187,3,123,84,130,56,171,49,184,23,145,116,158,207,93,1,129,191,9,191,20,97,141,157,248,79,139,78,94,12,94,119,181,160,224,57,99,155,159,119,236,70,186,22,187,242,217,126,77,107,99,121,255,189,49,92,138,121,233,43,57,233,225,75,184,222,201,178,73,46,68,74,199,66,221,121,172,47,78,238,245,233,43,197,240,65,149,226,35,132,177,19,239,158,0,4,158,120,146,203,55,81,119,42,101,87,245,244,254,16,123,189,3,75,158,16,133,221,14,228,27,237,91,1,63,179,38,226,198,24,159,148,158,228,202,201,173,219,92,10,228,80,160,143,32,79,219,47,114,90,149,168,14,241,23,83,169,113,118,58,248,120,72,40,53,41,82,228,46,89,89,218,105,136,178,192,204,180,213,83,206,61,46,219,240,26,223,233,76,102,158,189,142,120,100,4,87,83,51,12,168,91,150,193,176,251,169,125,12,180,207,210,192,38,46,219,122,237,205,105,229,134,163,187,186,159,14,197,163,223,214,49,186,184,209,87,58,116,113,59,237,54,199,155,124,251,98,225,84,111,116,171,105,115,211,100,121,28,197,225,11,209,31,50,241,51,129,150,6,62,9,218,31,89,14,211,16,226,69,81,163,40,173,80,225,214,2,209,70,239,98,85,165,150,220,38,243,224,177,98,25,249,126,152,86,176,99,76,108,108,205,126,201,115,200,89,169,5,230,6,250,14,120,31,110,53,31,36,224,2,248,144,13,161,3,225,165,228,120,50,228,123,127,5,47,96,87,70,206,216,59,225,179,112,255,217,247,125,164,3,26,86,204,70,102,88,21,98,230,20,226,250,109,219,157,218,27,212,254,250,181,144,28,252,128,218,191,248,209,60,195,42,68,204,171,196,5,84,248,65,26,185,208,176,40,37,222,55,255,126,37,201,192,100,82,6,61,16,68,37,196,253,217,190,198,122,128,249,184,135,192,17,47,88,16,179,55,252,211,246,186,132,174,209,222,126,153,174,2,65,138,30,170,153,234,185,135,163,104,164,23,94,146,137,33,39,49,15,127,203,171,88,32,116,224,30,120,213,230,244,223,251,233,59,223,38,223,213,18,159,196,108,252,250,61,197,244,78,99,95,191,200,40,11,153,194,150,254,240,128,235,141,248,112,102,109,223,163,61,13,57,121,102,238,110,238,28,2,25,245,212,232,62,15,149,44,93,129,39,95,159,109,83,202,112,237,173,229,117,73,19,174,205,78,133,49,40,227,79,62,120,166,107,158,49,158,18,99,9,134,73,86,43,174,211,174,66,235,90,18,62,111,2,196,62,72,172,126,93,54,222,95,158,117,172,108,141,162,165,103,65,199,231,228,3,23,231,156,128,22,220,143,66,159,47,142,187,96,99,196,86,10,177,92,208,19,50,202,190,168,248,178,240,169,213,102,47,156,0,252,180,181,4,195,95,43,228,109,126,145,140,28,128,10,23,159,75,67,125,59,60,165],\"1\":[83,65,246,139,120,241,89,167,110,53,191,214,181,241,95,138,116,18,215,61,136,85,72,206,226,46,122,92,137,61,216,236,218,169,193,142,197,23,181,176,152,243,239,130,249,47,51,75,7,205,187,221,111,2,172,232,181,242,176,67,144,57,133,81,197,75,250,129,180,120,199,201,208,184,107,11,176,131,180,88,23,202,55,247,248,187,57,186,98,203,228,250,88,124,220,254,15,192,234,191,182,15,183,223,135,210,159,151,236,131,152,27,23,17,118,195,11,187,148,206,131,114,14,2,226,117,24,142,136,168,220,196,199,221,111,115,153,123,128,122,247,81,204,8,220,90,155,188,236,59,65,152,44,60,136,113,48,97,209,227,152,73,4,216,160,27,8,162,168,83,154,119,122,38,94,173,175,69,38,164,234,229,62,5,13,14,15,1,39,140,30,10,138,223,237,158,153,205,87,69,25,41,113,152,38,153,174,169,54,25,68,173,224,117,170,79,224,52,206,43,122,183,92,9,28,193,194,237,49,172,231,249,29,197,208,138,2,90,255,32,71,83,64,138,220,194,187,226,178,108,94,249,28,34,129,0,116,241,26,1,118,15,34,152,45,102,249,122,179,43,56,95,169,127,91,12,162,106,193,65,250,27,163,205,202,188,220,64,47,109,174,132,92,33,23,231,108,126,36,57,9,188,162,167,224,65,106,66,204,191,212,127,179,72,241,67,192,153,58,151,216,15,199,89,112,111,130,98,213,66,113,222,215,65,225,208,91,12,195,226,5,250,2,219,250,248,189,221,62,33,88,98,189,206,236,39,223,153,28,236,94,185,209,221,93,133,146,15,127,204,142,16,96,36,198,87,52,247,69,65,188,29,216,170,247,28,250,36,25,12,28,194,204,88,127,216,73,209,180,146,86,47,201,138,250,146,183,143,88,107,60,128,157,34,188,240,107,157,249,11,235,85,229,147,183,19,54,220,196,85,3,28,205,67,87,214,182,7,223,7,244,229,138,189,104,156,97,101,11,155,211,81,205,125,46,57,12,172,27,36,55,124,6,247,149,202,54,39,5,58,198,212,77,218,149,39,205,47,196,116,98,213,65,221,243,23,196,129,95,130,116,19,217,27,86,46,224,166,151,222,134,150,59,50,70,151,97,232,96,89,255,27,77,244,128,51,198,184,126,29,193,57,35,79,231,84,97,55,132,145,60,112,113,122,122,172,34,37,135,21,252,186,14,31,91,154,137,147,125,254,149,45,176,14,146,67,167,191,101,210,69,247,158,250,174,232,51,9,67,156,48,223,214,149,12,240,32,174,210,167,2,185,175,72,136,133,122,35,160,175,147,121,1,209,179,62,223,100,199,228,114,209,14,88,250,107,249,195,102,183,180,78,234,237,95,96,255,22,232,7,43,8,109,226,77,93,218,95,20,37,109,170,160,78,53,224,48,187,92,143,7,155,132,88,136,56,178,225,34,70,221,28,251,125,206,138,36,137,180,4,161,133,87,219,122,185,241,216,25,154,139,60,89,190,184,194,89,105,195,148,135,38,158,141,60,192,174,164,188,175,129,30,247,232,203,128,170,135,184,44,174,84,102,164,203,115,206,125,73,243,42,201,249,222,214,140,198,85,129,65,90,154,156,101,102,103,165,70,63,38,130,131,73,86,112,105,246,148,14,165,58,252,45,57,181,64,118,241,142,167,137,63,102,207,118,154,48,241,203,138,107,25,139,29,16,3,230,30,105,57,161,136,128,125,156,56,105,47,42,125,156,106,80,102,89,8,55,104,215,254,110,132,213,46,28,61,81,114,95,30,186,7,142,15,180,102,61,203,34,132,50,133,13,58,40,251,27,115,206,165,182,82,109,179,26,109,47,40,196,244,83,27,219,178,95,92,237,210,83,182,178,161,102,214,80,193,246,44,1,65,168,82,250,244,129,202,141,70,164,74,55,178,156,73,191,131,79,27,50,21,128,44,6,45,173,41,245,193,53,183,99,75,104,138,206,196,43,68,16,153,141,126,200,43,74,168,222,48,145,243,67,118,56,146,104,159,136,119,209,183,224,113,123,8,210,242,247,18,170,77,189,236,141,181,225,102,21,167,54,167,91,112,25,57,40,5,21,89,57,117,111,78,24,38,0,94,229,200,17,143,216,237,141,69,164,48,235,169,234,153,255,167,12,200,251,164,10,245,198,6,187,81,192,157,65,171,131,3,27,178,60,254,62,28,68,5,22,189,200,105,140,95,139,152,136,149],\"8\":[163,17,66,120,5,244,188,246,165,142,192,57,214,175,164,32,233,218,67,224,33,165,156,16,32,188,39,211,111,46,82,235,71,80,21,214,155,148,89,121,7,68,112,113,116,189,107,103,178,148,41,80,67,173,8,142,38,245,240,85,36,249,103,208,105,42,193,97,157,51,101,110,248,247,98,222,57,75,195,184,79,10,191,153,211,144,177,97,92,250,61,176,230,190,159,43,63,1,0,31,202,109,239,242,251,129,58,10,148,97,254,13,214,179,167,7,179,228,255,113,46,97,21,118,141,84,46,251,179,0,172,26,31,219,144,132,121,77,241,108,218,124,119,255,105,67,254,45,131,5,223,6,194,74,11,93,249,36,80,232,54,156,208,230,14,56,83,53,243,227,253,254,41,174,155,59,18,105,153,53,33,69,84,228,29,173,54,152,205,111,55,3,203,151,160,177,161,154,80,30,89,87,154,151,42,0,185,42,242,171,213,250,148,243,73,98,30,162,136,188,8,46,141,154,25,238,142,27,14,162,190,195,204,86,113,158,70,181,141,248,45,90,170,158,111,167,200,174,97,21,111,214,251,68,91,37,247,217,133,47,120,45,194,234,168,233,85,120,104,71,151,137,155,244,118,78,81,194,217,57,74,239,25,112,202,176,15,156,84,72,194,20,181,180,198,57,169,1,90,24,85,118,227,199,151,213,47,39,47,128,82,58,237,123,144,188,221,253,139,154,154,159,64,207,238,71,60,136,116,159,180,44,209,50,199,106,178,179,254,71,21,22,7,120,167,52,191,82,231,106,177,44,158,164,103,175,21,237,220,20,156,241,251,243,108,64,113,95,206,251,81,101,28,107,247,2,75,159,5,175,99,14,160,179,141,115,153,118,189,42,108,93,195,204,168,8,187,146,205,52,248,105,91,5,30,71,239,27,176,10,147,50,206,242,207,195,221,220,94,134,71,149,8,29,60,193,113,237,178,82,248,98,214,230,242,122,253,128,125,189,133,252,101,231,66,209,253,211,13,9,185,136,2,151,100,175,85,87,251,60,201,43,75,6,118,133,247,43,187,240,65,206,13,180,196,16,164,118,106,142,50,180,64,17,230,139,235,197,225,158,145,105,188,156,117,144,110,248,204,245,105,133,73,213,245,146,70,19,15,180,232,42,214,234,221,183,158,209,126,29,139,75,94,93,52,153,158,249,195,197,15,75,187,53,114,168,253,77,239,34,39,188,63,205,85,243,7,186,193,147,78,71,132,129,97,242,127,219,222,135,253,102,200,116,239,71,191,137,75,47,37,1,37,223,169,127,30,213,125,153,193,254,136,99,44,82,141,198,246,211,79,34,128,216,193,100,117,137,43,217,83,143,207,83,99,180,42,253,214,124,247,9,215,139,220,75,166,217,92,55,71,242,169,32,1,59,225,204,98,90,223,175,71,28,91,201,196,42,227,186,170,67,193,32,242,8,137,183,94,150,120,32,42,22,141,172,56,38,68,155,128,133,191,183,67,127,61,247,3,60,187,210,6,38,243,51,17,9,84,216,241,95,219,116,66,153,41,133,178,11,240,148,170,225,93,248,250,226,119,52,201,33,17,120,198,155,201,170,244,130,122,88,162,131,104,172,167,184,236,82,202,249,6,144,123,170,186,67,154,236,40,71,81,102,91,98,227,160,217,7,241,193,116,215,146,164,123,210,134,208,141,4,180,223,54,83,112,67,163,16,138,33,110,160,191,12,164,237,131,14,223,179,53,218,172,128,184,225,92,109,58,53,136,167,79,239,2,88,5,188,132,232,101,104,9,80,3,252,25,52,170,72,134,81,166,226,201,170,110,39,108,173,21,154,74,89,165,47,214,115,91,10,183,245,146,193,179,113,226,119,124,61,8,202,15,218,139,23,56,246,56,21,236,2,246,161,212,86,150,175,142,163,236,54,61,102,138,190,187,230,90,53,52,202,17,42,149,210,97,59,6,152,172,184,208,140,119,35,47,246,126,159,198,99,27,233,108,144,45,134,9,155,130,95,94,210,64,141,178,107,133,101,26,228,156,109,217,211,232,53,184,188,44,33,104,122,108,204,21,75,167,199,222,186,197,58,0,37,16,71,119,252,197,251,25,53,132,9,243,31,14,246,177,196,37,30,168,217,47,147,88,104,186,174,32,87,13,53,184,198,217,195,53,221,66,3,152,227,232,147,79,58,22,98,188,137,220,14,171,121,42,158,123,4,164,2,209,5,36,31,158,250,201,25,5,67],\"3\":[9,40,233,153,173,55,216,205,244,126,162,110,157,130,69,251,118,87,249,182,222,27,140,244,48,27,127,46,243,185,193,244,102,201,100,171,44,161,215,111,174,202,73,173,216,139,200,116,153,158,234,28,105,94,102,77,193,13,133,94,232,11,229,252,233,80,74,79,136,9,65,174,221,137,94,25,149,229,107,147,241,227,221,58,38,190,67,128,163,203,12,83,236,227,81,49,52,27,17,184,212,139,108,42,15,243,12,189,83,113,106,106,98,48,185,119,115,29,171,206,191,50,202,4,27,128,99,211,141,218,35,151,154,248,84,129,193,154,4,184,108,216,62,92,86,39,226,4,142,213,224,82,123,211,254,40,14,241,3,113,69,227,66,195,117,107,23,65,197,71,243,243,38,99,13,171,120,255,125,95,230,244,222,156,110,215,248,226,73,163,210,12,169,99,60,106,212,207,86,80,111,86,230,90,10,207,50,255,211,63,92,203,40,176,95,83,255,167,192,229,33,96,166,170,230,147,69,143,13,36,150,45,242,46,139,211,75,61,164,87,43,133,238,108,186,157,127,67,7,140,21,96,225,197,179,108,88,148,114,100,82,114,174,66,125,195,78,242,116,29,203,43,122,123,244,168,9,80,0,212,132,190,15,142,107,86,127,2,20,75,226,106,143,136,232,58,35,223,48,97,231,47,12,243,115,72,186,39,249,250,4,37,87,76,209,0,119,95,249,110,153,186,111,238,64,116,8,77,129,184,169,166,202,186,135,195,233,189,44,208,122,243,95,138,88,144,46,234,65,62,39,199,209,128,212,122,222,165,67,166,213,157,29,130,239,52,3,47,24,17,93,78,142,202,222,160,79,67,70,107,138,200,167,254,183,245,230,230,79,221,169,95,113,229,170,72,4,132,6,40,133,131,85,165,51,230,91,181,181,135,3,89,26,43,174,74,154,196,227,91,251,64,32,176,39,245,110,79,158,160,9,89,189,232,224,16,203,41,69,243,119,241,12,91,222,26,237,49,87,208,242,176,44,91,70,51,19,216,70,196,38,79,45,220,17,113,18,114,169,151,253,45,30,85,50,228,162,51,91,172,46,201,238,189,102,244,160,129,188,165,193,104,218,193,224,27,239,8,133,95,168,185,163,207,190,105,10,60,8,235,82,160,210,212,17,40,129,6,34,81,35,25,132,236,202,14,150,76,111,77,64,217,86,35,248,30,2,2,226,115,244,209,20,76,19,133,86,208,3,49,71,124,156,222,16,31,49,200,52,92,213,88,100,7,222,184,50,223,83,223,43,56,165,123,137,45,87,150,142,154,253,154,109,193,42,190,170,125,86,100,0,93,248,10,248,151,218,219,55,252,183,214,219,56,4,215,8,130,146,18,175,159,146,64,171,193,27,19,211,92,221,195,224,23,44,195,176,225,173,169,169,151,42,115,59,176,174,85,117,200,200,216,78,241,50,199,136,75,59,193,93,239,166,146,172,112,151,146,21,65,23,25,193,4,109,87,160,133,2,159,97,114,225,208,3,123,105,90,48,189,148,83,99,194,1,103,167,116,238,182,150,133,233,247,82,201,127,173,24,154,37,98,247,203,32,95,171,95,200,92,132,230,254,34,243,253,113,126,199,15,205,195,57,210,3,183,192,89,108,31,227,178,155,0,31,187,233,184,159,142,221,235,101,9,249,145,154,120,12,222,24,178,31,96,252,137,28,0,96,73,25,233,229,169,115,93,97,152,54,228,236,148,243,224,97,137,154,86,42,51,6,240,4,94,85,60,182,215,80,252,144,98,148,23,157,66,222,164,22,42,40,14,66,130,94,182,224,113,183,64,140,252,114,118,93,136,48,20,34,127,19,95,97,137,99,121,14,124,5,24,251,215,92,187,225,228,218,172,40,78,197,166,104,57,162,192,174,119,94,137,11,152,68,184,243,143,122,139,85,230,22,190,226,251,146,182,89,218,89,180,96,250,21,112,231,76,213,119,132,2,162,150,79,206,17,241,61,20,172,188,164,108,114,38,96,227,244,228,134,48,133,86,9,123,125,209,51,245,87,142,246,197,136,81,230,145,101,105,186,154,189,82,244,177,228,63,88,78,139,109,157,71,248,180,7,211,192,242,99,118,176,103,189,225,31,167,96,231,120,243,162,161,6,175,155,59,31,148,86,137,18,97,213,170,80,254,67,68,228,47,59,195,99,46,86,227,99,133,42,52,6,80,48,192,135,118,83,200,127,99,101,165,97,153,27,188],\"6\":[90,149,140,53,121,148,108,180,117,11,173,24,107,54,142,171,218,60,71,105,217,215,43,46,173,123,191,107,122,83,66,74,253,162,177,49,80,137,8,35,213,158,73,223,188,123,59,201,123,174,197,146,239,180,13,217,165,229,173,243,106,162,100,177,1,14,59,7,47,76,29,116,141,60,194,240,73,224,215,233,186,21,191,4,201,128,204,182,157,240,55,244,80,184,209,247,147,96,177,231,215,11,75,148,176,242,112,67,169,38,94,30,133,37,184,98,184,58,123,227,189,112,98,198,128,212,41,23,68,243,236,117,174,69,20,97,48,168,128,254,239,203,132,172,190,92,230,154,40,213,88,131,131,56,159,119,154,125,114,228,200,121,81,155,195,164,47,236,1,67,34,253,191,80,179,143,19,91,31,1,38,104,233,193,205,135,2,243,31,132,228,206,40,224,178,228,206,115,84,87,172,81,10,169,230,171,58,128,15,98,150,231,84,233,198,41,21,23,68,17,242,222,46,142,246,177,46,250,72,108,21,55,129,136,105,168,199,12,19,44,49,217,222,203,89,19,98,191,229,48,145,132,109,44,96,23,223,11,146,16,43,30,55,118,24,222,52,255,223,158,113,105,0,111,80,140,236,34,90,102,177,175,161,154,218,185,139,253,227,105,53,178,242,136,30,199,77,103,100,187,68,187,112,221,113,255,15,205,83,237,111,56,5,121,65,205,100,102,172,63,228,29,201,41,64,99,226,235,69,42,186,161,131,186,231,39,125,244,245,74,168,16,218,179,247,182,26,156,49,191,85,196,226,16,8,104,82,19,64,52,174,200,62,56,76,190,94,182,32,61,168,223,131,147,146,227,213,205,31,16,169,70,7,68,175,4,172,45,129,11,83,47,228,62,97,144,197,64,97,7,254,152,244,33,102,73,137,50,75,98,203,250,228,208,20,205,207,52,69,45,223,58,71,128,55,116,132,58,97,201,235,116,198,25,157,226,89,188,52,28,167,232,217,219,157,150,132,175,136,37,28,104,119,42,170,138,68,155,216,236,119,200,205,16,84,253,178,3,178,25,239,250,136,214,109,237,185,216,245,166,209,14,161,220,233,238,95,38,144,29,42,72,245,85,204,230,186,102,149,41,208,35,207,71,163,130,18,11,65,15,36,253,44,126,247,209,2,39,46,142,45,215,131,226,58,104,236,144,107,58,252,224,105,125,251,191,104,13,200,240,157,154,58,207,73,219,235,131,18,202,186,66,201,190,3,0,45,82,232,23,243,7,154,243,128,175,247,240,172,131,114,213,57,70,254,3,44,226,19,174,51,81,69,58,194,170,244,161,187,239,238,37,29,175,237,196,234,252,179,107,233,237,39,93,68,155,75,234,45,187,209,165,153,151,35,11,247,98,94,16,86,117,4,90,109,27,198,128,2,44,253,191,107,76,167,84,137,64,197,122,168,18,20,97,28,128,241,41,6,94,134,25,66,96,215,245,134,203,174,215,32,80,62,22,116,226,145,2,244,165,171,103,12,71,226,213,59,2,246,143,196,252,124,70,104,136,227,185,244,39,126,7,37,201,117,106,107,90,157,195,134,140,220,31,240,42,114,123,207,123,204,69,226,13,93,31,250,142,108,63,92,132,136,16,70,241,34,16,24,206,139,218,133,140,1,200,193,150,88,3,153,109,196,50,3,233,230,228,116,221,133,195,104,67,95,27,2,141,19,35,28,205,176,163,6,119,208,11,104,57,233,41,50,3,93,135,150,147,174,130,131,34,14,167,175,155,244,184,71,96,36,63,148,39,45,119,20,182,97,143,44,237,184,53,200,7,225,217,233,23,94,129,225,206,36,229,85,130,139,57,212,130,101,182,78,205,201,43,29,23,18,183,176,108,134,52,180,219,67,166,10,85,36,123,9,13,89,237,65,6,58,51,117,133,123,4,1,190,159,197,9,140,217,204,239,2,43,231,70,196,114,153,231,17,254,19,80,75,61,218,142,66,157,71,172,94,8,32,7,133,222,166,32,34,126,225,230,163,128,124,199,118,149,212,109,117,39,255,133,163,171,33,175,194,233,143,224,114,237,34,33,151,250,210,213,118,135,57,4,177,51,204,39,81,248,239,253,102,249,199,110,102,241,170,30,29,93,163,77,192,27,71,132,225,108,66,5,64,20,148,63,66,121,126,136,126,44,82,188,221,155,250,151,101,32,97,218,213,126,152,139,88,3,53,177,220,10,91,205,108,54,208,79,252,23,23],\"2\":[35,58,103,185,133,5,217,87,173,98,13,139,28,66,70,181,216,126,92,223,91,190,162,214,239,23,164,194,176,197,187,46,223,24,241,122,159,20,161,75,168,116,153,227,60,9,164,69,215,225,21,15,121,50,18,42,105,0,140,158,59,18,48,161,182,220,105,162,6,60,90,88,80,17,172,255,170,59,81,90,221,141,228,102,77,42,120,203,127,233,67,19,71,188,64,46,225,20,69,228,0,206,104,12,140,232,183,201,52,236,145,173,45,46,29,211,183,16,91,153,47,48,184,93,12,232,33,231,250,248,204,69,3,211,4,100,171,68,207,74,121,146,190,83,251,66,148,116,160,35,114,124,68,225,167,23,132,159,253,71,68,235,198,117,96,69,96,51,85,81,209,96,152,94,102,11,195,75,105,16,134,238,218,103,242,82,47,23,9,172,51,53,2,193,16,169,105,211,112,33,198,167,42,68,79,14,1,167,88,172,194,52,89,244,140,160,47,182,212,237,165,57,209,233,52,208,154,231,86,116,48,30,76,19,7,20,124,94,202,187,253,133,167,92,81,29,142,141,120,44,126,39,6,24,54,180,248,189,110,200,175,178,254,135,137,167,84,10,206,109,66,20,230,186,46,190,91,170,167,216,187,129,217,34,184,224,225,216,102,144,105,137,16,244,188,254,130,53,108,176,126,166,2,207,182,252,60,15,97,8,69,248,58,92,184,25,19,186,18,77,227,140,17,248,169,101,134,109,1,102,238,197,118,232,234,12,70,212,99,83,223,126,226,55,8,18,228,67,65,121,44,90,48,97,195,244,109,0,55,170,19,192,242,180,148,84,200,101,86,9,237,188,12,171,166,184,27,202,144,53,44,126,129,126,198,143,37,107,51,202,25,20,164,49,36,126,68,38,32,12,224,203,203,67,196,43,139,98,246,226,226,114,28,53,16,105,79,163,40,212,21,228,3,20,20,218,29,98,57,64,117,127,99,201,143,101,78,19,7,4,252,131,109,27,189,204,27,10,96,174,53,126,251,10,164,19,21,81,182,40,123,53,60,227,133,120,108,26,149,25,77,67,125,118,83,18,164,118,94,248,206,224,125,103,188,184,51,109,80,118,99,86,194,78,163,32,45,64,106,174,58,45,0,75,54,67,118,236,171,113,12,26,148,77,57,146,66,12,245,53,172,25,214,205,252,112,230,100,127,32,217,104,124,183,88,237,175,175,88,126,87,194,208,190,89,219,67,206,151,88,217,112,56,119,57,167,1,250,239,190,196,136,21,225,244,236,119,28,253,204,243,241,56,99,163,199,186,254,41,119,103,102,241,208,152,237,210,161,32,93,35,238,213,255,208,33,201,172,74,73,224,172,133,216,145,30,208,186,222,81,192,151,183,154,102,233,30,3,95,196,41,89,248,132,255,101,5,143,151,14,122,189,219,115,234,104,191,163,4,190,129,129,200,90,155,111,113,213,125,170,12,138,185,23,110,149,41,135,209,122,136,87,239,115,204,198,170,246,79,218,227,144,148,23,199,235,214,1,58,44,105,49,151,140,80,37,249,124,113,59,204,203,134,34,179,94,231,149,142,126,12,232,168,214,114,11,62,39,59,96,51,190,5,128,184,92,163,177,99,130,6,89,114,221,40,175,174,5,177,112,160,28,196,20,3,32,68,221,181,104,92,250,191,89,97,29,241,225,107,144,235,212,173,120,71,21,213,185,12,231,154,58,20,151,205,122,175,220,198,131,18,68,91,207,83,248,148,165,28,135,122,205,213,202,227,226,121,121,182,144,191,146,97,158,128,11,119,233,128,106,195,128,105,42,171,55,179,9,5,210,118,50,225,38,171,197,14,97,11,131,27,250,67,107,95,147,53,1,93,62,153,199,149,95,22,200,222,143,80,70,253,205,111,5,245,59,105,254,206,251,238,182,120,56,94,160,71,120,57,28,51,199,215,93,10,220,118,87,38,148,10,244,254,37,114,130,236,122,86,88,62,194,26,101,31,194,226,46,26,90,75,167,185,221,146,117,47,152,133,140,178,17,52,253,142,174,168,223,87,111,132,225,105,192,181,169,7,186,14,19,236,152,11,160,143,4,129,181,115,88,61,156,3,194,84,7,222,137,196,192,168,117,122,160,132,131,103,113,59,129,112,35,8,229,111,44,66,176,252,64,34,243,76,30,146,116,232,47,166,100,246,215,36,12,145,229,129,253,29,6,129,151,28,172,205,109,128,77,115,9,180,145,226,7,211,100]},\"reachable\":{\"{\\\"addr\\\":[49,57,49,46,49,56,48,46,49,48,51,46,50,51,54,0],\\\"port\\\":50309,\\\"uid_fingerprint\\\":17576062521251310994,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,70,68,56,68,67,55,69,70,57,50,70,49,70,49,52,68,48,48,67,70,57,68,54,70,54,50,57,55,65,51,52,54,56,66,53,57,69,55,48,55,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[6,0],[7,0]],\"{\\\"addr\\\":[49,51,50,46,49,56,49,46,49,53,53,46,55,54,0,0],\\\"port\\\":55241,\\\"uid_fingerprint\\\":4215620865531854407,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,67,66,70,56,51,51,51,49,54,53,70,49,67,51,53,65,68,70,48,54,66,70,52,50,53,49,48,52,69,56,51,51,65,68,68,65,49,54,53,66,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[1,1]],\"{\\\"addr\\\":[49,53,54,46,55,50,46,50,49,57,46,52,57,0,0,0],\\\"port\\\":56030,\\\"uid_fingerprint\\\":8564141605929456761,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,68,56,66,67,67,48,69,67,70,69,50,67,57,54,53,65,66,67,50,57,67,54,50,55,70,68,67,69,67,48,66,48,54,67,53,65,68,66,49,54,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[1,2]],\"{\\\"addr\\\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\\\"port\\\":63174,\\\"uid_fingerprint\\\":922068108643293997,\\\"info\\\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,70,68,56,68,67,55,69,70,57,50,70,49,70,49,52,68,48,48,67,70,57,68,54,70,54,50,57,55,65,51,52,54,56,66,53,57,69,55,48,55,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]}\":[[6,1],[8,0]],\"{\\\"addr\\\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\\\"port\\\":63174,\\\"uid_fingerprint\\\":9189545078772697644,\\\"info\\\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,69,68,67,51,65,65,48,51,70,69,54,69,53,50,66,69,55,55,56,68,68,52,54,70,50,55,68,51,53,56,57,57,51,66,55,65,49,48,65,52,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]}\":[[6,2],[9,0]],\"{\\\"addr\\\":[50,49,54,46,49,49,55,46,51,46,54,50,0,0,0,0],\\\"port\\\":63174,\\\"uid_fingerprint\\\":14821306939685929169,\\\"info\\\":[116,121,112,101,61,115,99,114,97,109,98,108,101,115,117,105,116,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,66,48,56,49,49,56,65,56,48,68,55,54,54,66,65,57,65,56,70,57,65,55,50,50,67,50,54,54,49,52,69,48,54,66,56,54,69,52,67,65,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,83,111,109,101,40,123,34,112,97,115,115,119,111,114,100,34,58,32,34,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,50,51,52,53,54,55,34,125,41,0]}\":[[10,0]],\"{\\\"addr\\\":[49,51,52,46,50,48,55,46,50,51,53,46,49,48,53,0],\\\"port\\\":9042,\\\"uid_fingerprint\\\":5838789323103363859,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,56,48,55,56,70,57,55,66,50,50,49,53,55,69,66,55,49,54,65,69,66,68,53,54,65,57,52,55,54,67,51,55,56,57,57,65,68,70,54,65,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[2,2],[5,0]],\"{\\\"addr\\\":[50,50,46,50,52,49,46,56,49,46,54,50,0,0,0,0],\\\"port\\\":36457,\\\"uid_fingerprint\\\":14409951350199027285,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,68,68,56,56,57,53,48,56,48,48,50,70,57,50,53,67,70,55,52,69,49,51,48,68,49,48,49,67,68,70,49,67,49,51,51,65,65,65,68,49,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[10,2]],\"{\\\"addr\\\":[49,57,53,46,55,50,46,55,52,46,49,49,55,0,0,0],\\\"port\\\":28746,\\\"uid_fingerprint\\\":8013544061081454931,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,50,57,49,49,48,66,55,68,57,69,57,54,48,65,53,67,52,50,56,52,56,55,55,50,65,70,54,50,52,65,51,66,50,55,65,50,68,69,52,69,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[2,0],[3,0]],\"{\\\"addr\\\":[49,50,56,46,49,51,57,46,52,52,46,49,53,55,0,0],\\\"port\\\":10839,\\\"uid_fingerprint\\\":16072135124933872676,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,55,67,53,65,51,68,67,53,51,49,48,55,53,53,48,54,65,50,52,52,65,51,67,70,49,57,66,53,55,70,52,68,69,52,55,55,67,65,54,51,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[2,1],[4,0]],\"{\\\"addr\\\":[50,49,50,46,49,54,54,46,49,54,54,46,54,55,0,0],\\\"port\\\":32414,\\\"uid_fingerprint\\\":3643046579853129326,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,66,48,56,49,49,56,65,56,48,68,55,54,54,66,65,57,65,56,70,57,65,55,50,50,67,50,54,54,49,52,69,48,54,66,56,54,69,52,67,65,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,102,97,115,116,34,58,32,116,114,117,101,44,32,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[1,0]],\"{\\\"addr\\\":[57,55,46,49,55,53,46,57,49,46,55,0,0,0,0,0],\\\"port\\\":27719,\\\"uid_fingerprint\\\":9270304952963857555,\\\"info\\\":[116,121,112,101,61,111,98,102,115,50,32,98,108,111,99,107,101,100,95,105,110,61,123,125,32,112,114,111,116,111,99,111,108,61,116,99,112,32,102,105,110,103,101,114,112,114,105,110,116,61,34,69,68,67,51,65,65,48,51,70,69,54,69,53,50,66,69,55,55,56,68,68,52,54,70,50,55,68,51,53,56,57,57,51,66,55,65,49,48,65,52,34,32,111,114,95,97,100,100,114,101,115,115,101,115,61,78,111,110,101,32,100,105,115,116,114,105,98,117,116,105,111,110,61,104,116,116,112,115,32,102,108,97,103,115,61,83,111,109,101,40,123,34,115,116,97,98,108,101,34,58,32,116,114,117,101,44,32,34,114,117,110,110,105,110,103,34,58,32,116,114,117,101,44,32,34,118,97,108,105,100,34,58,32,116,114,117,101,44,32,34,102,97,115,116,34,58,32,116,114,117,101,125,41,32,112,97,114,97,109,115,61,78,111,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}\":[[10,1]]},\"spares\":[10,1],\"unallocated_bridges\":[],\"recycleable_keys\":[],\"blocked_keys\":[],\"open_inv_keys\":[[3,2460237],[4,2460237],[5,2460237],[7,2460237],[8,2460237],[9,2460237]],\"date_last_enc\":2460237},\"trustup_migration_table\":{\"table\":{\"5\":2,\"4\":2,\"9\":6,\"7\":6,\"3\":2,\"8\":6},\"migration_type\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},\"blockage_migration_table\":{\"table\":{},\"migration_type\":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},\"openinv_filter\":{\"seen_table\":[[167,121,14,179,249,174,210,194,223,78,92,229,191,6,227,23,111,17,206,87,132,3,0,86,116,37,13,177,246,57,9,0],[20,146,141,79,128,206,56,95,212,130,37,81,177,11,14,184,9,71,14,116,101,107,243,17,237,199,192,127,212,63,30,7]]},\"id_filter\":{\"seen_table\":[]},\"inv_id_filter\":{\"seen_table\":[]},\"trust_promotion_filter\":{\"seen_table\":[]}},\"extra_bridges\":[],\"to_be_replaced_bridges\":[]}"; + let test_context: LoxServerContext = serde_json::from_str(&test_string).unwrap(); + lox_db.write_context(test_context); + assert!( + lox_db.db.len() == 1, + "db should have written only one context" + ); + } +} diff --git a/crates/lox-distributor/src/file_reader.rs b/crates/lox-distributor/src/file_reader.rs deleted file mode 100644 index 3a0846f..0000000 --- a/crates/lox-distributor/src/file_reader.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::lox_context; -use chrono::prelude::*; -use std::{ - env, - error::Error, - fs::{DirEntry, File}, - io::BufReader, - path::Path, -}; - -pub fn read_context_from_file>( - path: P, -) -> Result> { - let file = File::open(path)?; - let reader = BufReader::new(file); - let context = serde_json::from_reader(reader)?; - Ok(context) -} - -pub fn write_context_to_file(context: lox_context::LoxServerContext) { - let mut date = Local::now().format("%Y-%m-%d_%H:%M:%S").to_string(); - let path = "_lox.json"; - date.push_str(path); - let file = File::create(&date) - .expect(format!("Unable to write to db file: {:?} !", stringify!($date)).as_str()); - let _ = serde_json::to_writer(file, &context); -} - -pub fn check_db_exists() -> Option { - let current_path = env::current_dir().expect("Unable to access current dir"); - std::fs::read_dir(current_path) - .expect("Couldn't read local directory") - .flatten() // Remove failed - .filter(|f| { - f.metadata().unwrap().is_file() - && (f.file_name().into_string().unwrap().contains("_lox.json")) - }) // Filter out directories (only consider files) - .max_by_key(|x| x.metadata().unwrap().modified().unwrap()) -} diff --git a/crates/lox-distributor/src/lox_context.rs b/crates/lox-distributor/src/lox_context.rs index 38d67cd..30c1708 100644 --- a/crates/lox-distributor/src/lox_context.rs +++ b/crates/lox-distributor/src/lox_context.rs @@ -6,25 +6,212 @@ use lox_library::{ blockage_migration, check_blockage, issue_invite, level_up, migration, open_invite, redeem_invite, trust_promotion, }, - BridgeAuth, BridgeDb, IssuerPubKey, + BridgeAuth, BridgeDb, ExceededMaxBridgesError, IssuerPubKey, }; +use rdsys_backend::proto::{Resource, ResourceState}; use serde::{Deserialize, Serialize}; use std::{ + cmp::Ordering, collections::HashMap, sync::{Arc, Mutex}, }; use zkp::ProofError; -#[derive(Clone, Serialize, Deserialize)] +use crate::metrics::Metrics; +use crate::resource_parser::{parse_into_bridgelines, sort_for_parsing}; + +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct LoxServerContext { pub db: Arc>, pub ba: Arc>, pub extra_bridges: Arc>>, pub to_be_replaced_bridges: Arc>>, + #[serde(skip)] + pub metrics: Metrics, } impl LoxServerContext { + pub fn bridgetable_is_empty(&self) -> bool { + let mut ba_obj = self.ba.lock().unwrap(); + ba_obj.is_empty() + } + + // Populate an empty bridgetable for the first time + pub fn populate_bridgetable( + &self, + buckets: Vec<[BridgeLine; MAX_BRIDGES_PER_BUCKET]>, + percent_spares: i32, + ) { + let mut partition: i32 = 0; + if percent_spares != 0 { + partition = buckets.len() as i32 * percent_spares / 100; + } + let (spares, open_invitations) = buckets.split_at(partition as usize); + for bucket in spares { + self.add_spare_bucket(*bucket) + } + + for bucket in open_invitations { + self.add_openinv_bucket(*bucket) + } + } + + pub fn handle_working_resources(&self, working_resources: Vec) -> Vec { + let mut accounted_for_bridges: Vec = Vec::new(); + let bridgelines = parse_into_bridgelines(working_resources); + for bridge in bridgelines { + /* TODO: Functionality for marking bridges as unreachable/blocked should eventually happen here. + It is currently not enabled as there is not yet a reliable way to determine that a bridge is blocked. + This means that migrations to unblocked bridges do not currently work but can be easily enabled by parsing the + list of `blocked resources` from rdsys or another source with something like the following: + let res = context.add_unreachable(bridgeline); + if res { + println!( + "BridgeLine {:?} successfully marked unreachable: {:?}", + bridgeline + ); + } else { + println!( + "BridgeLine {:?} NOT marked unreachable, saved for next update!", + bridge.uid_fingerprint + ); + } + */ + let res = self.update_bridge(bridge); + if res { + println!( + "BridgeLine {:?} successfully updated.", + bridge.uid_fingerprint + ); + accounted_for_bridges.push(bridge.uid_fingerprint); + self.metrics.existing_or_updated_bridges.inc(); + // Assume non-failing bridges that are not found in the bridge table are new bridges and save them for later + } else { + println!("BridgeLine: {:?} not found in Lox's Bridgetable. Save it as a new resource for now!", bridge.uid_fingerprint); + self.append_extra_bridges(bridge); + self.metrics.new_bridges.inc(); + } + } + accounted_for_bridges + } + + pub fn handle_not_working_resources( + &self, + not_working_resources: Vec, + mut accounted_for_bridges: Vec, + ) -> Vec { + let (grace_period, failing) = sort_for_parsing(not_working_resources); + // Update bridges in the bridge table that are failing but within the grace period + for bridge in grace_period { + let res = self.update_bridge(bridge); + if res { + println!( + "BridgeLine {:?} successfully updated.", + bridge.uid_fingerprint + ); + accounted_for_bridges.push(bridge.uid_fingerprint); + self.metrics.existing_or_updated_bridges.inc(); + } + } + // Next, handle the failing bridges. If resource last passed tests >=ACCEPTED_HOURS_OF_FAILURE ago, + // it should be replaced with a working resource and be removed from the bridgetable. + for bridge in failing { + let res = self.replace_with_new(bridge); + if res == lox_library::ReplaceSuccess::Replaced { + println!( + "BridgeLine {:?} successfully replaced.", + bridge.uid_fingerprint + ); + accounted_for_bridges.push(bridge.uid_fingerprint); + self.metrics.removed_bridges.inc(); + } else if res == lox_library::ReplaceSuccess::NotReplaced { + // Add the bridge to the list of to_be_replaced bridges in the Lox context and try + // again to replace at the next update (nothing changes in the Lox Authority) + println!( + "BridgeLine {:?} NOT replaced, saved for next update!", + bridge.uid_fingerprint + ); + self.new_to_be_replaced_bridge(bridge); + self.metrics.existing_or_updated_bridges.inc(); + accounted_for_bridges.push(bridge.uid_fingerprint); + } else { + // NotFound + assert!( + res == lox_library::ReplaceSuccess::NotFound, + "ReplaceSuccess incorrectly set" + ); + println!( + "BridgeLine {:?} no longer in bridge table.", + bridge.uid_fingerprint + ); + } + } + accounted_for_bridges + } + + // Sync resources received from rdsys with the Lox bridgetable + pub fn sync_with_bridgetable(&self, resources: ResourceState) { + // Check if each resource is already in the Lox bridgetable. If it is, it's probably fine + // to replace the existing resource with the incoming one to account for changes + // save a list of accounted for bridges and deal with the unaccounted for bridges at the end + let mut accounted_for_bridges: Vec = Vec::new(); + // ensure all working resources are updated and accounted for + if let Some(working_resources) = resources.working { + accounted_for_bridges = self.handle_working_resources(working_resources); + } + if let Some(not_working_resources) = resources.not_working { + accounted_for_bridges = + self.handle_not_working_resources(not_working_resources, accounted_for_bridges); + } + let mut ba_clone = self.ba.lock().unwrap(); + let total_reachable = ba_clone.bridge_table.reachable.len(); + match total_reachable.cmp(&accounted_for_bridges.len()) { + Ordering::Greater => { + let unaccounted_for = ba_clone.find_and_remove_unaccounted_for_bridges(accounted_for_bridges); + for bridgeline in unaccounted_for { + match self.replace_with_new(bridgeline) { + lox_library::ReplaceSuccess::Replaced => { + println!("BridgeLine {:?} not found in rdsys update was successfully replaced.", bridgeline.uid_fingerprint); + self.metrics.removed_bridges.inc(); + } + lox_library::ReplaceSuccess::NotReplaced => { + // Add the bridge to the list of to_be_replaced bridges in the Lox context and try + // again to replace at the next update (nothing changes in the Lox Authority) + println!("BridgeLine {:?} not found in rdsys update NOT replaced, saved for next update!", + bridgeline.uid_fingerprint); + self.new_to_be_replaced_bridge(bridgeline); + self.metrics.existing_or_updated_bridges.inc(); + } + lox_library::ReplaceSuccess::NotFound => println!( + "BridgeLine {:?} no longer in reachable bridges.", + bridgeline.uid_fingerprint + ), + } + } + } + Ordering::Less => println!("Something unexpected occurred: The number of reachable bridges should not be less than those updated from rdsys"), + _ => (), + + } + // Finally, assign any extra_bridges to new buckets if there are enough + while self.extra_bridges.lock().unwrap().len() >= MAX_BRIDGES_PER_BUCKET { + let bucket = self.remove_extra_bridges(); + // TODO: Decide the circumstances under which a bridge is allocated to an open_inv or spare bucket, + // eventually also do some more fancy grouping of new resources, i.e., by type or region + let mut db_obj = self.db.lock().unwrap(); + match ba_clone.add_spare_bucket(bucket, &mut db_obj) { + Ok(_) => (), + Err(e) => { + println!("Error: {:?}", e); + for bridge in bucket { + self.append_extra_bridges(bridge); + } + } + } + } + } + pub fn append_extra_bridges(&self, bridge: BridgeLine) { let mut extra_bridges = self.extra_bridges.lock().unwrap(); extra_bridges.push(bridge); @@ -87,6 +274,8 @@ impl LoxServerContext { } } + // Attempt to remove a bridge that is failing tests and replace it with a bridge from the + // available bridges or from a spare bucket pub fn replace_with_new(&self, bridgeline: BridgeLine) -> lox_library::ReplaceSuccess { let mut ba_obj = self.ba.lock().unwrap(); let eb_obj = self.extra_bridges.lock().unwrap(); @@ -100,7 +289,7 @@ impl LoxServerContext { result } - /* Uncomment when bridge blocking is finalized + /* TODO: Uncomment when bridge blocking is finalized pub fn add_unreachable(&self, bridgeline: BridgeLine) -> bool { let mut ba_obj = self.ba.lock().unwrap(); let mut db_obj = self.db.lock().unwrap(); @@ -143,10 +332,16 @@ impl LoxServerContext { self.ba.lock().unwrap().today() } - fn gen_invite(&self) -> lox_utils::Invite { + fn gen_invite(&self) -> Result { let mut obj = self.db.lock().unwrap(); - lox_utils::Invite { - invite: obj.invite(), + match obj.invite() { + Ok(invite) => { + if obj.current_k == 1 { + self.metrics.k_reset_count.inc(); + } + Ok(lox_utils::Invite { invite }) + } + Err(e) => Err(e), } } @@ -207,9 +402,16 @@ impl LoxServerContext { // Generate and return an open invitation token pub fn generate_invite(self) -> Response { + self.metrics.invites_requested.inc(); let invite = self.gen_invite(); - match serde_json::to_string(&invite) { - Ok(resp) => prepare_header(resp), + match invite { + Ok(invite) => match serde_json::to_string(&invite) { + Ok(resp) => prepare_header(resp), + Err(e) => { + println!("Error parsing Invite to JSON"); + prepare_error_header(e.to_string()) + } + }, Err(e) => { println!("Error parsing Invite to JSON"); prepare_error_header(e.to_string()) @@ -261,6 +463,7 @@ impl LoxServerContext { match self.open_inv(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.open_inv_count.inc(); prepare_header(response) } Err(e) => { @@ -278,6 +481,7 @@ impl LoxServerContext { match self.trust_promo(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.trust_promo_count.inc(); prepare_header(response) } Err(e) => { @@ -295,6 +499,7 @@ impl LoxServerContext { match self.trust_migration(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.trust_mig_count.inc(); prepare_header(response) } Err(e) => { @@ -312,6 +517,7 @@ impl LoxServerContext { match self.level_up(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.level_up_count.inc(); prepare_header(response) } Err(e) => { @@ -329,6 +535,7 @@ impl LoxServerContext { match self.issue_invite(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.issue_invite_count.inc(); prepare_header(response) } Err(e) => { @@ -346,6 +553,7 @@ impl LoxServerContext { match self.redeem_invite(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.redeem_invite_count.inc(); prepare_header(response) } Err(e) => { @@ -363,6 +571,7 @@ impl LoxServerContext { match self.check_blockage(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.check_blockage_count.inc(); prepare_header(response) } Err(e) => { @@ -380,6 +589,7 @@ impl LoxServerContext { match self.blockage_migration(req) { Ok(resp) => { let response = serde_json::to_string(&resp).unwrap(); + self.metrics.blockage_migration_count.inc(); prepare_header(response) } Err(e) => { diff --git a/crates/lox-distributor/src/main.rs b/crates/lox-distributor/src/main.rs index 018661c..290110a 100644 --- a/crates/lox-distributor/src/main.rs +++ b/crates/lox-distributor/src/main.rs @@ -1,40 +1,38 @@ -use chrono::Utc; use clap::Parser; use futures::future; -use futures::StreamExt; use hyper::{ server::conn::AddrStream, service::{make_service_fn, service_fn}, Body, Request, Response, Server, }; -use lox_library::bridge_table::{BridgeLine, MAX_BRIDGES_PER_BUCKET}; -use lox_library::{BridgeAuth, BridgeDb}; -use rdsys_backend::{proto::ResourceDiff, start_stream}; +use prometheus_client::registry::Registry; +use rdsys_backend::{proto::ResourceState, request_resources}; use serde::Deserialize; use std::{ convert::Infallible, fs::File, io::BufReader, - net::SocketAddr, + net::{IpAddr, Ipv4Addr, SocketAddr}, path::PathBuf, - sync::{Arc, Mutex}, time::Duration, }; -mod file_reader; -use file_reader::{check_db_exists, read_context_from_file, write_context_to_file}; +mod db_handler; +use db_handler::DB; mod lox_context; +mod metrics; +use metrics::Metrics; mod request_handler; use request_handler::handle; mod resource_parser; -use resource_parser::parse_resource; +use resource_parser::{parse_into_bridgelines, parse_into_buckets}; use tokio::{ signal, spawn, sync::{broadcast, mpsc, oneshot}, - time::sleep, + time::{interval, sleep}, }; async fn shutdown_signal() { @@ -51,11 +49,46 @@ struct Args { #[arg(short, long, default_value = "config.json")] config: PathBuf, - /// Optional name/path of the lox context json backup file - /// Used to populate the Lox context. If none is provided, an empty - /// Lox context will be created + // Optional Date/time to roll back to as a %Y-%m-%d_%H:%M:%S string + // This argument should be passed if the lox_context should be rolled back to a + // previous state due to, for example, a mass blocking event that is likely not + // due to Lox user behaviour. If the exact roll back date/time is not known, the + // last db entry within 24 hours from the passed roll_back_date will be used or else + // the program will fail gracefully. #[arg(short, long, verbatim_doc_comment)] - backup_context: Option, + roll_back_date: Option, +} + +#[derive(Debug, Deserialize)] +struct Config { + db: DbConfig, + metrics_port: u16, + bridge_config: BridgeConfig, + rtype: ResourceInfo, +} + +// Path of the lox database +#[derive(Debug, Deserialize)] +pub struct DbConfig { + // The path for the lox_context database, default is "lox_db" + db_path: String, +} + +impl Default for DbConfig { + fn default() -> DbConfig { + DbConfig { + db_path: "lox_db".to_owned(), + } + } +} + +// Config information for how bridges should be allocated to buckets +#[derive(Debug, Default, Deserialize)] +pub struct BridgeConfig { + // The percentage of buckets (made up of MAX_BRIDGES_PER_BUCKET bridges) + // that should be allocated as spare buckets + // This will be calculated as the floor of buckets.len() * percent_spares / 100 + percent_spares: i32, } #[derive(Debug, Deserialize)] @@ -69,32 +102,37 @@ struct ResourceInfo { // Rdsys sender creates a ResourceStream with the api_endpoint, resource token and type specified // in the config.json file. -// TODO: ensure this stream gracefully shutdowns on the ctrl_c command. async fn rdsys_stream( rtype: ResourceInfo, - tx: mpsc::Sender, + tx: mpsc::Sender, mut kill: broadcast::Receiver<()>, ) { - let mut rstream = start_stream(rtype.endpoint, rtype.name, rtype.token, rtype.types) - .await - .expect("rdsys stream initialization failed. Start rdsys or check config.json"); - loop { - tokio::select! { - res = rstream.next() => { - match res { - Some(diff) => tx.send(diff).await.unwrap(), - None => return, - } - }, - _ = kill.recv() => {println!("Shut down rdsys stream"); return}, + tokio::select! { + start_resource_request = rdsys_request(rtype, tx) => start_resource_request, + _ = kill.recv() => {println!("Shut down rdsys request loop")}, - } + } +} + +async fn rdsys_request(rtype: ResourceInfo, tx: mpsc::Sender) { + let mut interval = interval(Duration::from_secs(5)); + loop { + interval.tick().await; + let resources = request_resources( + rtype.endpoint.clone(), + rtype.name.clone(), + rtype.token.clone(), + rtype.types.clone(), + ) + .await + .unwrap(); + tx.send(resources).await.unwrap(); } } async fn rdsys_bridge_parser( rdsys_tx: mpsc::Sender, - rx: mpsc::Receiver, + rx: mpsc::Receiver, mut kill: broadcast::Receiver<()>, ) { tokio::select! { @@ -103,24 +141,38 @@ async fn rdsys_bridge_parser( } } -// Parse Bridges receives a ResourceDiff from rdsys_sender and sends it to the -// Context Manager to be parsed and added to the BridgeDB -async fn parse_bridges(rdsys_tx: mpsc::Sender, mut rx: mpsc::Receiver) { +// Parse Bridges receives the resources from rdsys and sends it to the +// Context Manager to be parsed and added to the Lox BridgeDB +async fn parse_bridges(rdsys_tx: mpsc::Sender, mut rx: mpsc::Receiver) { loop { - let resourcediff = rx.recv().await.unwrap(); - let cmd = Command::Rdsys { resourcediff }; + let resources = rx.recv().await.unwrap(); + let cmd = Command::Rdsys { resources }; rdsys_tx.send(cmd).await.unwrap(); sleep(Duration::from_secs(1)).await; } } +async fn start_metrics_collector( + metrics_addr: SocketAddr, + registry: Registry, + mut kill: broadcast::Receiver<()>, +) { + tokio::select! { + lox_metrics = metrics::start_metrics_server(metrics_addr, registry) => lox_metrics, + _ = kill.recv() => {println!("Shut down metrics server");}, + } +} + async fn create_context_manager( - backup_context_path: Option, + db_config: DbConfig, + bridge_config: BridgeConfig, + roll_back_date: Option, + metrics: Metrics, context_rx: mpsc::Receiver, mut kill: broadcast::Receiver<()>, ) { tokio::select! { - create_context = context_manager(backup_context_path, context_rx) => create_context, + create_context = context_manager(db_config, bridge_config, roll_back_date, metrics, context_rx) => create_context, _ = kill.recv() => {println!("Shut down context_manager");}, } } @@ -128,170 +180,50 @@ async fn create_context_manager( // Context Manager handles the Lox BridgeDB and Bridge Authority, ensuring // that the DB can be updated from the rdsys stream and client requests // can be responded to with an updated BridgeDB state -async fn context_manager(db_path: Option, mut context_rx: mpsc::Receiver) { - let context: lox_context::LoxServerContext; - if let Some(existing_db) = db_path.as_deref() { - context = read_context_from_file(existing_db).unwrap(); - } else if let Some(last_modified_file) = check_db_exists() { - println!("Reading from file {:?}", last_modified_file); - context = read_context_from_file(&last_modified_file.path()).unwrap(); - } else { - let new_db = BridgeDb::new(); - let new_ba = BridgeAuth::new(new_db.pubkey); - context = lox_context::LoxServerContext { - db: Arc::new(Mutex::new(new_db)), - ba: Arc::new(Mutex::new(new_ba)), - extra_bridges: Arc::new(Mutex::new(Vec::new())), - to_be_replaced_bridges: Arc::new(Mutex::new(Vec::new())), - } - } +async fn context_manager( + db_config: DbConfig, + bridge_config: BridgeConfig, + roll_back_date: Option, + metrics: Metrics, + mut context_rx: mpsc::Receiver, +) { + let (mut lox_db, context) = + match DB::open_new_or_existing_db(db_config, roll_back_date, metrics) { + Ok((lox_db, context)) => (lox_db, context), + Err(e) => { + panic!("Error: {:?}", e); + } + }; while let Some(cmd) = context_rx.recv().await { use Command::*; match cmd { - Rdsys { resourcediff } => { - if let Some(new_resources) = resourcediff.new { - let mut count = 0; - let mut bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET]; - for pt in new_resources { - println!("A NEW RESOURCE: {:?}", pt); - if let Some(resources) = pt.1 { - for resource in resources { - let bridgeline = parse_resource(resource); - println!("Now it's a bridgeline: {:?}", bridgeline); - if context.to_be_replaced_bridges.lock().unwrap().len() > 0 { - println!("BridgeLine to be replaced: {:?}", bridgeline); - let res = context.replace_with_new(bridgeline); - if res == lox_library::ReplaceSuccess::NotFound { - println!( - "BridgeLine not found in bridge_table, already updated {:?}", - bridgeline - ); - } else if res == lox_library::ReplaceSuccess::Replaced { - println!( - "BridgeLine successfully replaced: {:?}", - bridgeline - ); - } else { - assert!( - res == lox_library::ReplaceSuccess::NotReplaced, - "ReplaceSuccess incorrectly set somehow" - ); - // Add the bridge to the list of to_be_replaced bridges in the Lox context and try - // again to replace at the next update (nothing changes in the Lox Authority) - println!("'Gone' BridgeLine NOT replaced, saved for next update! : {:?}", bridgeline); - context.new_to_be_replaced_bridge(bridgeline); - } - } else if count < MAX_BRIDGES_PER_BUCKET { - bucket[count] = bridgeline; - count += 1; - } else { - // TODO: Decide the circumstances under which a bridge is allocated to an open_inv or spare bucket, - // eventually also do some more fancy grouping of new resources, i.e., by type or region - context.add_openinv_bucket(bucket); - count = 0; - bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET]; - } - } - } - } - // Handle the extra buckets that were not allocated already - if count != 0 { - for val in 0..count { - if context.extra_bridges.lock().unwrap().len() - < (MAX_BRIDGES_PER_BUCKET) - { - context.append_extra_bridges(bucket[val]); - } else { - bucket = context.remove_extra_bridges(); - context.add_spare_bucket(bucket); - } + Rdsys { resources } => { + // If the bridgetable is not being loaded from an existing database, we will populate the + // bridgetable with all of the working bridges received from rdsys. + if context.bridgetable_is_empty() { + if let Some(working_resources) = resources.working { + let bridgelines = parse_into_bridgelines(working_resources); + context.metrics.new_bridges.inc_by(bridgelines.len() as u64); + let (buckets, leftovers) = parse_into_buckets(bridgelines); + for leftover in leftovers { + context.append_extra_bridges(leftover); } + context.populate_bridgetable(buckets, bridge_config.percent_spares); + + // otherwise, we need to sync the existing bridgetable with the resources we receive from + // rdsys and ensure that all functioning bridges are correctly placed in the bridgetable + // those that have changed are updated and those that have been failing tests for an extended + // period of time are removed. + // If bridges are labelled as blocked_in, we should also handle blocking behaviour. } + } else { + context.sync_with_bridgetable(resources); } - if let Some(changed_resources) = resourcediff.changed { - for pt in changed_resources { - println!("A NEW CHANGED RESOURCE: {:?}", pt); - if let Some(resources) = pt.1 { - for resource in resources { - let bridgeline = parse_resource(resource); - println!("BridgeLine to be changed: {:?}", bridgeline); - let res = context.update_bridge(bridgeline); - if res { - println!("BridgeLine successfully updated: {:?}", bridgeline); - } else { - println!("BridgeLine: {:?} not found in Lox's Bridgetable. Save it as a new resource for now!", bridgeline); - if context.extra_bridges.lock().unwrap().len() < 2 { - context.append_extra_bridges(bridgeline); - } else { - let bucket = context.remove_extra_bridges(); - context.add_spare_bucket(bucket); - } - } - } - } - } - } - // gone resources are not the same as blocked resources. - // Instead, these are bridges which have either failed to pass tests for some period - // or have expired bridge descriptors. In both cases, the bridge is unusable, but this - // is not likely due to censorship. Therefore, we replace gone resources with new resources - // TODO: create a notion of blocked resources from information collected through various means: - // https://gitlab.torproject.org/tpo/anti-censorship/censorship-analysis/-/issues/40035 - if let Some(gone_resources) = resourcediff.gone { - for pt in gone_resources { - println!("A NEW GONE RESOURCE: {:?}", pt); - if let Some(resources) = pt.1 { - for resource in resources { - // If resource last passed tests 3 hours ago, it should be replaced with a working - // resource and be removed from the bridgetable. If it has been gone for more than 7 hours, - // we should stop trying to remove it from the bridge table and assume it has successfully been - // removed already - if resource.last_passed < (Utc::now() - chrono::Duration::hours(3)) - || resource.last_passed - > (Utc::now() - chrono::Duration::hours(7)) - { - let bridgeline = parse_resource(resource); - println!("BridgeLine to be replaced: {:?}", bridgeline); - let res = context.replace_with_new(bridgeline); - if res == lox_library::ReplaceSuccess::Replaced { - println!( - "BridgeLine successfully replaced: {:?}", - bridgeline - ); - } else if res == lox_library::ReplaceSuccess::NotReplaced { - // Add the bridge to the list of to_be_replaced bridges in the Lox context and try - // again to replace at the next update (nothing changes in the Lox Authority) - println!( - "'Gone' BridgeLine NOT replaced, saved for next update! : {:?}", - bridgeline - ); - context.new_to_be_replaced_bridge(bridgeline); - } - } - } - } - } - } - /* Functionality for marking bridges as unreachable/blocked is currently not enabled as there is not - yet a reliable way to determine that a bridge is blocked. This means that migrations to unblocked bridges do not - currently work but can be easily enabled with a list of `blocked resources` from rdsys or another source with something - like the following: - println!("BridgeLine to be removed: {:?}", bridgeline); - let res = context.add_unreachable(bridgeline); - if res { - println!( - "BridgeLine successfully marked unreachable: {:?}", - bridgeline - ); - } else { - println!("'Gone' BridgeLine NOT REMOVED!! : {:?}", bridgeline); - //TODO probably do something else here - } - */ + // Handle any bridges that are leftover in the bridge authority from the sync context.allocate_leftover_bridges(); context.encrypt_table(); - write_context_to_file(context.clone()); + lox_db.write_context(context.clone()); sleep(Duration::from_millis(1)).await; } Request { req, sender } => { @@ -299,9 +231,11 @@ async fn context_manager(db_path: Option, mut context_rx: mpsc::Receive if let Err(e) = sender.send(response) { eprintln!("Server Response Error: {:?}", e); }; + lox_db.write_context(context.clone()); sleep(Duration::from_millis(1)).await; } Shutdown { shutdown_sig } => { + lox_db.write_context(context.clone()); println!("Sending Shutdown Signal, all threads should shutdown."); drop(shutdown_sig); println!("Shutdown Sent."); @@ -314,7 +248,7 @@ async fn context_manager(db_path: Option, mut context_rx: mpsc::Receive #[derive(Debug)] enum Command { Rdsys { - resourcediff: ResourceDiff, + resources: ResourceState, }, Request { req: Request, @@ -332,8 +266,7 @@ async fn main() { let file = File::open(&args.config).expect("Could not read config file"); let reader = BufReader::new(file); // Read the JSON contents of the file as a ResourceInfo - let rtype: ResourceInfo = - serde_json::from_reader(reader).expect("Reading ResourceInfo from JSON failed."); + let config: Config = serde_json::from_reader(reader).expect("Reading Config from JSON failed."); let (rdsys_tx, context_rx) = mpsc::channel(32); let request_tx = rdsys_tx.clone(); @@ -342,6 +275,7 @@ async fn main() { // create the shutdown broadcast channel and clone for every thread let (shutdown_tx, mut shutdown_rx) = broadcast::channel(16); let kill_stream = shutdown_tx.subscribe(); + let kill_metrics = shutdown_tx.subscribe(); let kill_parser = shutdown_tx.subscribe(); let kill_context = shutdown_tx.subscribe(); @@ -360,12 +294,27 @@ async fn main() { } }); + let metrics = Metrics::default(); + let registry = metrics.register(); + let metrics_addr = + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), config.metrics_port); + let metrics_handler = + spawn(async move { start_metrics_collector(metrics_addr, registry, kill_metrics).await }); + let context_manager = spawn(async move { - create_context_manager(args.backup_context, context_rx, kill_context).await + create_context_manager( + config.db, + config.bridge_config, + args.roll_back_date, + metrics, + context_rx, + kill_context, + ) + .await }); let (tx, rx) = mpsc::channel(32); - let rdsys_stream_handler = spawn(async { rdsys_stream(rtype, tx, kill_stream).await }); + let rdsys_request_handler = spawn(async { rdsys_stream(config.rtype, tx, kill_stream).await }); let rdsys_resource_receiver = spawn(async { rdsys_bridge_parser(rdsys_tx, rx, kill_parser).await }); @@ -395,7 +344,8 @@ async fn main() { eprintln!("server error: {}", e); } future::join_all([ - rdsys_stream_handler, + metrics_handler, + rdsys_request_handler, rdsys_resource_receiver, context_manager, shutdown_handler, diff --git a/crates/lox-distributor/src/metrics.rs b/crates/lox-distributor/src/metrics.rs new file mode 100644 index 0000000..2dcc4a3 --- /dev/null +++ b/crates/lox-distributor/src/metrics.rs @@ -0,0 +1,187 @@ +use hyper::{ + service::{make_service_fn, service_fn}, + Body, Request, Response, Server, +}; +use prometheus_client::{encoding::text::encode, metrics::counter::Counter, registry::Registry}; +use std::{future::Future, io, net::SocketAddr, pin::Pin, sync::Arc}; +use tokio::signal::unix::{signal, SignalKind}; + +#[derive(Debug, Clone)] +pub struct Metrics { + pub existing_or_updated_bridges: Counter, + pub new_bridges: Counter, + pub removed_bridges: Counter, + pub blocked_bridges: Counter, + pub open_inv_count: Counter, + pub trust_promo_count: Counter, + pub trust_mig_count: Counter, + pub level_up_count: Counter, + pub issue_invite_count: Counter, + pub redeem_invite_count: Counter, + pub check_blockage_count: Counter, + pub blockage_migration_count: Counter, + pub k_reset_count: Counter, + pub invites_requested: Counter, +} + +impl Default for Metrics { + fn default() -> Self { + // Create counters. + let existing_or_updated_bridges = Counter::default(); + let new_bridges = Counter::default(); + let removed_bridges = Counter::default(); + let blocked_bridges = Counter::default(); + let open_inv_count = Counter::default(); + let trust_promo_count = Counter::default(); + let trust_mig_count = Counter::default(); + let level_up_count = Counter::default(); + let issue_invite_count = Counter::default(); + let redeem_invite_count = Counter::default(); + let check_blockage_count = Counter::default(); + let blockage_migration_count = Counter::default(); + let k_reset_count = Counter::default(); + let invites_requested = Counter::default(); + + Metrics { + existing_or_updated_bridges, + new_bridges, + removed_bridges, + blocked_bridges, + open_inv_count, + trust_promo_count, + trust_mig_count, + level_up_count, + issue_invite_count, + redeem_invite_count, + check_blockage_count, + blockage_migration_count, + k_reset_count, + invites_requested, + } + } +} + +impl Metrics { + pub fn register(&self) -> Registry { + // Create a Registry and register Counter. + let mut r = ::with_prefix("lox-metrics"); + r.register( + "existing_or_updated_bridges", + "number of existing or updated bridges recorded at rdsys sync", + self.existing_or_updated_bridges.clone(), + ); + r.register( + "new_bridges", + "number of new bridges added to bridge table", + self.new_bridges.clone(), + ); + r.register( + "removed_bridges", + "number of bridges removed from the bridgetable", + self.removed_bridges.clone(), + ); + r.register( + "blocked_bridges", + "number of bridges blocked", + self.blocked_bridges.clone(), + ); + r.register( + "open_inv_counter", + "number of open invitations distributed", + self.open_inv_count.clone(), + ); + r.register( + "trust_promo_counter", + "number of trust promotions requests", + self.trust_promo_count.clone(), + ); + r.register( + "trust_mig_counter", + "number of trust migrations requests", + self.trust_mig_count.clone(), + ); + r.register( + "level_up_counter", + "number of level up requests", + self.level_up_count.clone(), + ); + r.register( + "issue_invite_counter", + "number of issue invite requests", + self.issue_invite_count.clone(), + ); + r.register( + "redeem_invite_counter", + "number of level up requests", + self.redeem_invite_count.clone(), + ); + r.register( + "check_blockage_counter", + "number of check blockage requests", + self.check_blockage_count.clone(), + ); + r.register( + "blockage_migration_counter", + "number of blockage migration requests", + self.blockage_migration_count.clone(), + ); + r.register( + "k_reset_counter", + "number of times k has reset to 0", + self.k_reset_count.clone(), + ); + r.register( + "invites_requested", + "number of invites requested", + self.invites_requested.clone(), + ); + r + } +} + +/// Start a HTTP server to report metrics. +pub async fn start_metrics_server(metrics_addr: SocketAddr, registry: Registry) { + let mut shutdown_stream = signal(SignalKind::terminate()).unwrap(); + + eprintln!("Starting metrics server on {metrics_addr}"); + + let registry = Arc::new(registry); + Server::bind(&metrics_addr) + .serve(make_service_fn(move |_conn| { + let registry = registry.clone(); + async move { + let handler = make_handler(registry); + Ok::<_, io::Error>(service_fn(handler)) + } + })) + .with_graceful_shutdown(async move { + shutdown_stream.recv().await; + }) + .await + .unwrap(); +} + +/// This function returns a HTTP handler (i.e. another function) +pub fn make_handler( + registry: Arc, +) -> impl Fn(Request) -> Pin>> + Send>> { + // This closure accepts a request and responds with the OpenMetrics encoding of our metrics. + move |_req: Request| { + let reg = registry.clone(); + Box::pin(async move { + let mut buf = String::new(); + encode(&mut buf, ®.clone()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)) + .map(|_| { + let body = Body::from(buf); + Response::builder() + .header( + hyper::header::CONTENT_TYPE, + "application/openmetrics-text; version=1.0.0; charset=utf-8", + ) + .body(body) + .unwrap() + }) + }) + } +} diff --git a/crates/lox-distributor/src/request_handler.rs b/crates/lox-distributor/src/request_handler.rs index d2b4c46..61deb45 100644 --- a/crates/lox-distributor/src/request_handler.rs +++ b/crates/lox-distributor/src/request_handler.rs @@ -74,9 +74,11 @@ pub async fn handle( #[cfg(test)] mod tests { use crate::lox_context; + use crate::metrics::Metrics; use super::*; + use base64::{engine::general_purpose, Engine as _}; use chrono::{Duration, Utc}; use julianday::JulianDay; use lox_library::{ @@ -235,6 +237,7 @@ mod tests { ba: Arc::new(Mutex::new(lox_auth)), extra_bridges: Arc::new(Mutex::new(Vec::new())), to_be_replaced_bridges: Arc::new(Mutex::new(Vec::new())), + metrics: Metrics::default(), }; Self { context } } @@ -298,7 +301,7 @@ mod tests { rng.fill_bytes(&mut cert); let infostr: String = format!( "obfs4 cert={}, iat-mode=0", - base64::encode_config(cert, base64::STANDARD_NO_PAD) + general_purpose::STANDARD_NO_PAD.encode(cert) ); res.info[..infostr.len()].copy_from_slice(infostr.as_bytes()); res diff --git a/crates/lox-distributor/src/resource_parser.rs b/crates/lox-distributor/src/resource_parser.rs index 7a541b3..9136450 100644 --- a/crates/lox-distributor/src/resource_parser.rs +++ b/crates/lox-distributor/src/resource_parser.rs @@ -1,13 +1,19 @@ -use lox_library::bridge_table::{BridgeLine, BRIDGE_BYTES}; +use chrono::{Duration, Utc}; +use lox_library::bridge_table::{BridgeLine, BRIDGE_BYTES, MAX_BRIDGES_PER_BUCKET}; use rdsys_backend::proto::Resource; -pub fn parse_resource(resource: Resource) -> BridgeLine { - let mut ip_bytes: [u8; 16] = [0; 16]; - ip_bytes[..resource.address.len()].copy_from_slice(resource.address.as_bytes()); - let resource_uid = resource - .get_uid() - .expect("Unable to get Fingerprint UID of resource"); - let infostr: String = format!( +pub const ACCEPTED_HOURS_OF_FAILURE: i64 = 3; + +// Parse each resource from rdsys into a Bridgeline as expected by the Lox Bridgetable +pub fn parse_into_bridgelines(resources: Vec) -> Vec { + let mut bridgelines: Vec = Vec::new(); + for resource in resources { + let mut ip_bytes: [u8; 16] = [0; 16]; + ip_bytes[..resource.address.len()].copy_from_slice(resource.address.as_bytes()); + let resource_uid = resource + .get_uid() + .expect("Unable to get Fingerprint UID of resource"); + let infostr: String = format!( "type={} blocked_in={:?} protocol={} fingerprint={:?} or_addresses={:?} distribution={} flags={:?} params={:?}", resource.r#type, resource.blocked_in, @@ -18,13 +24,167 @@ pub fn parse_resource(resource: Resource) -> BridgeLine { resource.flags, resource.params, ); - let mut info_bytes: [u8; BRIDGE_BYTES - 26] = [0; BRIDGE_BYTES - 26]; + let mut info_bytes: [u8; BRIDGE_BYTES - 26] = [0; BRIDGE_BYTES - 26]; - info_bytes[..infostr.len()].copy_from_slice(infostr.as_bytes()); - BridgeLine { - addr: ip_bytes, - port: resource.port, - uid_fingerprint: resource_uid, - info: info_bytes, + info_bytes[..infostr.len()].copy_from_slice(infostr.as_bytes()); + bridgelines.push(BridgeLine { + addr: ip_bytes, + port: resource.port, + uid_fingerprint: resource_uid, + info: info_bytes, + }) + } + bridgelines +} + +// Allocate each Bridgeline into a bucket that will later be allocated into spare buckets or open invitation buckets +// Any leftover buckets from total_bridgelines % MAX_BRIDGES_PER_BUCKET are returned in a separate Vec +// TODO: Improve this function to sort bridgelines into buckets in a more intentional manner. This could include +// sorting bridgelines with high bandwidth into buckets that are only distributed to more trusted users or sorting +// bridgelines by location +pub fn parse_into_buckets( + mut bridgelines: Vec, +) -> (Vec<[BridgeLine; MAX_BRIDGES_PER_BUCKET]>, Vec) { + let mut buckets: Vec<[BridgeLine; MAX_BRIDGES_PER_BUCKET]> = Vec::new(); + let mut count = 0; + let mut bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET]; + let mut leftovers: Vec = Vec::new(); + for bridgeline in bridgelines.clone() { + println!( + "Added bridge with fingerprint: {:?}", + bridgeline.uid_fingerprint + ); + if count < MAX_BRIDGES_PER_BUCKET { + bucket[count] = bridgeline; + count += 1; + } else { + buckets.push(bucket); + count = 0; + bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET]; + } + } + // Handle the extra buckets that were not allocated already + if count != 0 { + for _ in 0..count { + // Assumes that the unallocated bridgelines will be the last x of the passed bridgelines + leftovers.push(bridgelines.pop().unwrap()); + } + } + (buckets, leftovers) +} + +// Sort Resources into those that are functional and those that are failing based on the last time +// they were passing tests. Before passing them back to the calling function, they are parsed into +// BridgeLines +pub fn sort_for_parsing(resources: Vec) -> (Vec, Vec) { + let mut grace_period: Vec = Vec::new(); + let mut failing: Vec = Vec::new(); + for resource in resources { + // TODO: Maybe filter for untested resources first if last_passed alone would skew + // the filter in an unintended direction + if resource.test_result.last_passed + Duration::hours(ACCEPTED_HOURS_OF_FAILURE) + >= Utc::now() + { + grace_period.push(resource); + } else { + failing.push(resource); + } + } + let grace_period_bridgelines = parse_into_bridgelines(grace_period); + let failing_bridgelines = parse_into_bridgelines(failing); + + (grace_period_bridgelines, failing_bridgelines) +} + +#[cfg(test)] +mod tests { + use rdsys_backend::proto::{Resource, TestResults}; + use std::collections::HashMap; + + use chrono::{Duration, Utc}; + + use super::sort_for_parsing; + + pub fn make_resource( + rtype: String, + address: String, + port: u16, + fingerprint: String, + last_passed: i64, + ) -> Resource { + let mut flags = HashMap::new(); + flags.insert(String::from("fast"), true); + flags.insert(String::from("stable"), true); + let mut params = HashMap::new(); + params.insert( + String::from("password"), + String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), + ); + Resource { + r#type: String::from(rtype), + blocked_in: HashMap::new(), + test_result: TestResults { + last_passed: Utc::now() - Duration::hours(last_passed), + }, + protocol: String::from("tcp"), + address: String::from(address), + port: port, + fingerprint: String::from(fingerprint), + or_addresses: None, + distribution: String::from("https"), + flags: Some(flags), + params: Some(params), + } + } + + #[test] + fn test_sort_for_parsing() { + let resource_one = make_resource( + "scramblesuit".to_owned(), + "123.456.789.100".to_owned(), + 3002, + "BE84A97D02130470A1C77839954392BA979F7EE1".to_owned(), + 2, + ); + let resource_two = make_resource( + "https".to_owned(), + "123.222.333.444".to_owned(), + 6002, + "C56B9EF202130470A1C77839954392BA979F7FF9".to_owned(), + 5, + ); + let resource_three = make_resource( + "scramblesuit".to_owned(), + "444.888.222.100".to_owned(), + 3042, + "1A4C8BD902130470A1C77839954392BA979F7B46".to_owned(), + 4, + ); + let resource_four = make_resource( + "https".to_owned(), + "555.444.212.100".to_owned(), + 8022, + "FF024DC302130470A1C77839954392BA979F7AE2".to_owned(), + 3, + ); + let resource_five = make_resource( + "https".to_owned(), + "234.111.212.100".to_owned(), + 10432, + "7B4DE14CB2130470A1C77839954392BA979F7AE2".to_owned(), + 1, + ); + let mut test_vec: Vec = Vec::new(); + test_vec.push(resource_one); + test_vec.push(resource_two); + test_vec.push(resource_three); + test_vec.push(resource_four); + test_vec.push(resource_five); + let (functional, failing) = sort_for_parsing(test_vec); + assert!( + functional.len() == 2, + "There should be 2 functional bridges" + ); + assert!(failing.len() == 3, "There should be 3 failing bridges"); } } diff --git a/crates/lox-library/Cargo.toml b/crates/lox-library/Cargo.toml index 30c6cda..647f32c 100644 --- a/crates/lox-library/Cargo.toml +++ b/crates/lox-library/Cargo.toml @@ -9,28 +9,24 @@ description = "Main Lox library with protocols and functions that that make up L keywords = ["tor", "lox", "bridges"] [dependencies] -curve25519-dalek = { package = "curve25519-dalek-ng", version = "3", default-features = false, features = ["serde", "std"] } -ed25519-dalek = { version = "1", features = ["serde"] } -# zkp = { version = "0.8", features = ["debug-transcript"] } -zkp = "0.8" +curve25519-dalek = { version = "4", default-features = false, features = ["serde", "rand_core", "digest"] } +ed25519-dalek = { version = "2", features = ["serde", "rand_core"] } bincode = "1" chrono = "0.4" -rand = "0.7" -serde = "1.0.188" -serde_with = {version = "3.3.0", features = ["json"]} -sha2 = "0.9" +rand = { version = "0.8", features = ["std_rng"]} +serde = "1.0.190" +serde_with = {version = "3.4.0", features = ["json"]} +sha2 = "0.10" statistical = "1.0.0" lazy_static = "1" hex_fmt = "0.3" -aes-gcm = "0.8" -base64 = "0.13" -time = "0.3.28" -subtle = "2.4" -thiserror = "1.0.48" +aes-gcm = { version = "0.10", features =["aes"]} +base64 = "0.21" +time = "0.3.30" +prometheus = "0.13.3" +subtle = "2.5" +thiserror = "1.0.49" +zkp = { git = "https://gitlab.torproject.org/onyinyang/lox-zkp" } [features] -default = ["u64_backend"] -u32_backend = ["curve25519-dalek/u32_backend"] -u64_backend = ["curve25519-dalek/u64_backend"] -simd_backend = ["curve25519-dalek/simd_backend"] fast = [] diff --git a/crates/lox-library/src/bridge_table.rs b/crates/lox-library/src/bridge_table.rs index d820bd0..43e248b 100644 --- a/crates/lox-library/src/bridge_table.rs +++ b/crates/lox-library/src/bridge_table.rs @@ -11,8 +11,9 @@ use super::cred; use super::IssuerPrivKey; use super::CMZ_B_TABLE; use aes_gcm::aead; -use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead}; -use aes_gcm::Aes128Gcm; +use aes_gcm::aead::{generic_array::GenericArray, Aead}; +use aes_gcm::{Aes128Gcm, KeyInit}; +use base64::{engine::general_purpose, Engine as _}; use curve25519_dalek::ristretto::CompressedRistretto; use curve25519_dalek::ristretto::RistrettoBasepointTable; use curve25519_dalek::scalar::Scalar; @@ -149,8 +150,12 @@ impl BridgeLine { let date = u32::from_le_bytes(data[pos..pos + 4].try_into().unwrap()); let (optP, optQ) = if date > 0 { ( - CompressedRistretto::from_slice(&data[pos + 4..pos + 36]).decompress(), - CompressedRistretto::from_slice(&data[pos + 36..]).decompress(), + CompressedRistretto::from_slice(&data[pos + 4..pos + 36]) + .expect("Unable to extract P from bucket") + .decompress(), + CompressedRistretto::from_slice(&data[pos + 36..]) + .expect("Unable to extract Q from bucket") + .decompress(), ) } else { (None, None) @@ -170,6 +175,7 @@ impl BridgeLine { (bridges, None) } } + /// Create a random BridgeLine for testing #[cfg(test)] pub fn random() -> Self { @@ -197,7 +203,7 @@ impl BridgeLine { rng.fill_bytes(&mut cert); let infostr: String = format!( "obfs4 cert={}, iat-mode=0", - base64::encode_config(cert, base64::STANDARD_NO_PAD) + general_purpose::STANDARD_NO_PAD.encode(cert) ); res.info[..infostr.len()].copy_from_slice(infostr.as_bytes()); res diff --git a/crates/lox-library/src/dup_filter.rs b/crates/lox-library/src/dup_filter.rs index 6f874ba..2bd747a 100644 --- a/crates/lox-library/src/dup_filter.rs +++ b/crates/lox-library/src/dup_filter.rs @@ -4,8 +4,7 @@ This implementation just keeps the table of seen ids in memory, but a production one would of course use a disk-backed database. */ -use std::cmp::Eq; -use std::collections::HashMap; +use std::collections::HashSet; use std::hash::Hash; use serde::{Deserialize, Serialize}; @@ -14,7 +13,7 @@ use serde::{Deserialize, Serialize}; /// seen ids. IdType will typically be Scalar. #[derive(Default, Debug, Serialize, Deserialize)] pub struct DupFilter { - seen_table: HashMap, + seen_table: HashSet, } /// A return type indicating whether the item was fresh (not previously @@ -30,7 +29,7 @@ impl DupFilter { /// to the seen table. Return Seen if it is already in the table, /// Fresh if not. pub fn check(&self, id: &IdType) -> SeenType { - if self.seen_table.contains_key(id) { + if self.seen_table.contains(id) { SeenType::Seen } else { SeenType::Fresh @@ -41,9 +40,9 @@ impl DupFilter { /// table, and add it if not. Return Fresh if it was not already /// in the table, and Seen if it was. pub fn filter(&mut self, id: &IdType) -> SeenType { - match self.seen_table.insert(*id, ()) { - None => SeenType::Fresh, - Some(()) => SeenType::Seen, + match self.seen_table.insert(*id) { + true => SeenType::Fresh, + false => SeenType::Seen, } } } diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index 8a5de08..ee75858 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -22,6 +22,7 @@ pub mod cred; pub mod dup_filter; pub mod migration_table; +use chrono::Duration; use chrono::{DateTime, Utc}; use sha2::Sha512; @@ -36,7 +37,7 @@ use rand::Rng; use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; -use ed25519_dalek::{Keypair, PublicKey, Signature, SignatureError, Signer, Verifier}; +use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey}; use subtle::ConstantTimeEq; use std::collections::HashSet; @@ -57,7 +58,7 @@ lazy_static! { pub static ref CMZ_B: RistrettoPoint = dalek_constants::RISTRETTO_BASEPOINT_POINT; pub static ref CMZ_A_TABLE: RistrettoBasepointTable = RistrettoBasepointTable::create(&CMZ_A); pub static ref CMZ_B_TABLE: RistrettoBasepointTable = - dalek_constants::RISTRETTO_BASEPOINT_TABLE; + dalek_constants::RISTRETTO_BASEPOINT_TABLE.clone(); } // EXPIRY_DATE is set to EXPIRY_DATE days for open-entry and blocked buckets in order to match @@ -78,6 +79,12 @@ pub enum NoAvailableIDError { ExhaustedIndexer, } +#[derive(Error, Debug)] +pub enum ExceededMaxBridgesError { + #[error("The maximum number of bridges has already been distributed today, please try again tomorrow!")] + ExceededMaxBridges, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct IssuerPrivKey { x0tilde: Scalar, @@ -123,20 +130,26 @@ impl IssuerPubKey { } } +// Number of times a given invitation is ditributed pub const OPENINV_K: u32 = 10; +// TODO: Decide on maximum daily number of invitations to be distributed +pub const MAX_DAILY_BRIDGES: u32 = 100; /// The BridgeDb. This will typically be a singleton object. The /// BridgeDb's role is simply to issue signed "open invitations" to /// people who are not yet part of the system. #[derive(Debug, Serialize, Deserialize)] pub struct BridgeDb { /// The keypair for signing open invitations - keypair: Keypair, + keypair: SigningKey, /// The public key for verifying open invitations - pub pubkey: PublicKey, + pub pubkey: VerifyingKey, /// The set of open-invitation buckets openinv_buckets: HashSet, distributed_buckets: Vec, - current_k: u32, + #[serde(skip)] + today: DateTime, + pub current_k: u32, + pub daily_bridges_distributed: u32, } /// An open invitation is a [u8; OPENINV_LENGTH] where the first 32 @@ -152,14 +165,16 @@ impl BridgeDb { /// Create the BridgeDb. pub fn new() -> Self { let mut csprng = OsRng {}; - let keypair = Keypair::generate(&mut csprng); - let pubkey = keypair.public; + let keypair = SigningKey::generate(&mut csprng); + let pubkey = keypair.verifying_key(); Self { keypair, pubkey, openinv_buckets: Default::default(), distributed_buckets: Default::default(), + today: Utc::now(), current_k: 0, + daily_bridges_distributed: 0, } } @@ -189,30 +204,39 @@ impl BridgeDb { /// Produce an open invitation such that the next k users, where k is < /// OPENINV_K, will receive the same open invitation bucket /// chosen randomly from the set of open-invitation buckets. - pub fn invite(&mut self) -> [u8; OPENINV_LENGTH] { + pub fn invite(&mut self) -> Result<[u8; OPENINV_LENGTH], ExceededMaxBridgesError> { let mut res: [u8; OPENINV_LENGTH] = [0; OPENINV_LENGTH]; let mut rng = rand::thread_rng(); // Choose a random invitation id (a Scalar) and serialize it let id = Scalar::random(&mut rng); res[0..32].copy_from_slice(&id.to_bytes()); let bucket_num: u32; - if self.current_k < OPENINV_K && !self.distributed_buckets.is_empty() { - bucket_num = *self.distributed_buckets.last().unwrap(); - self.current_k += 1; - } else { - // Choose a random bucket number (from the set of open - // invitation buckets) and serialize it - let openinv_vec: Vec<&u32> = self.openinv_buckets.iter().collect(); - bucket_num = *openinv_vec[rng.gen_range(0, openinv_vec.len())]; - self.mark_distributed(bucket_num); - self.remove_openinv(&bucket_num); - self.current_k = 1; + if Utc::now() >= (self.today + Duration::days(1)) { + self.today = Utc::now(); + self.daily_bridges_distributed = 0; + } + if self.daily_bridges_distributed < MAX_DAILY_BRIDGES { + if self.current_k < OPENINV_K && !self.distributed_buckets.is_empty() { + bucket_num = *self.distributed_buckets.last().unwrap(); + self.current_k += 1; + } else { + // Choose a random bucket number (from the set of open + // invitation buckets) and serialize it + let openinv_vec: Vec<&u32> = self.openinv_buckets.iter().collect(); + bucket_num = *openinv_vec[rng.gen_range(0..openinv_vec.len())]; + self.mark_distributed(bucket_num); + self.remove_openinv(&bucket_num); + self.current_k = 1; + self.daily_bridges_distributed += 1; + } + res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes()); + // Sign the first 36 bytes and serialize it + let sig = self.keypair.sign(&res[0..(32 + 4)]); + res[(32 + 4)..].copy_from_slice(&sig.to_bytes()); + Ok(res) + } else { + Err(ExceededMaxBridgesError::ExceededMaxBridges) } - res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes()); - // Sign the first 36 bytes and serialize it - let sig = self.keypair.sign(&res[0..(32 + 4)]); - res[(32 + 4)..].copy_from_slice(&sig.to_bytes()); - res } /// Verify an open invitation. Returns the invitation id and the @@ -221,7 +245,7 @@ impl BridgeDb { /// before. pub fn verify( invitation: [u8; OPENINV_LENGTH], - pubkey: PublicKey, + pubkey: VerifyingKey, ) -> Result<(Scalar, u32), SignatureError> { // Pull out the signature and verify it let sig = Signature::try_from(&invitation[(32 + 4)..])?; @@ -229,11 +253,13 @@ impl BridgeDb { // The signature passed. Pull out the bucket number and then // the invitation id let bucket = u32::from_le_bytes(invitation[32..(32 + 4)].try_into().unwrap()); - match Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap()) { + let s = Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap()); + if s.is_some().into() { + return Ok((s.unwrap(), bucket)); + } else { // It should never happen that there's a valid signature on // an invalid serialization of a Scalar, but check anyway. - None => Err(SignatureError::new()), - Some(s) => Ok((s, bucket)), + return Err(SignatureError::new()); } } } @@ -269,7 +295,7 @@ pub struct BridgeAuth { pub invitation_pub: IssuerPubKey, /// The public key of the BridgeDb issuing open invitations - pub bridgedb_pub: PublicKey, + pub bridgedb_pub: VerifyingKey, /// The bridge table pub bridge_table: BridgeTable, @@ -294,7 +320,7 @@ pub struct BridgeAuth { } impl BridgeAuth { - pub fn new(bridgedb_pub: PublicKey) -> Self { + pub fn new(bridgedb_pub: VerifyingKey) -> Self { // Create the private and public keys for each of the types of // credential, each with the appropriate number of attributes let lox_priv = IssuerPrivKey::new(6); @@ -330,6 +356,10 @@ impl BridgeAuth { } } + pub fn is_empty(&mut self) -> bool { + self.bridge_table.buckets.is_empty() + } + /// Insert a set of open invitation bridges. /// /// Each of the bridges will be given its own open invitation @@ -376,19 +406,17 @@ impl BridgeAuth { Ok(()) } - // TODO Ensure synchronization of Lox bridge_table with rdsys - pub fn sync_table(&mut self) { - - // Create a hashtable (?) of bridges in the lox distributor from new resources - // accept the hashtable and recreate the bridge table from the hash table here - // using existing reachable bridges, other table checks and placements from existing bridge table - // If bridges are in reachable bridges, put them in the table with their Vec - // How to check for bridges that aren't there/are extra? - // After going through the update, make sure bridges in the table are the same and deal with discrepencies - // This will be the bad/annoying part - - //also use open_inv_keys and blocked_keys from bridge_table to remove expired keys from table. - // make sure this happens before they are removed from the structures in the bridge table + pub fn find_and_remove_unaccounted_for_bridges( + &mut self, + accounted_for_bridges: Vec, + ) -> Vec { + let mut unaccounted_for: Vec = Vec::new(); + for (k, _v) in self.bridge_table.reachable.clone() { + if !accounted_for_bridges.contains(&k.uid_fingerprint) { + unaccounted_for.push(k); + } + } + unaccounted_for } pub fn allocate_bridges( @@ -428,16 +456,11 @@ impl BridgeAuth { let reachable_bridges = self.bridge_table.reachable.clone(); for reachable_bridge in reachable_bridges { if reachable_bridge.0.uid_fingerprint == bridge.uid_fingerprint { - println!( - "Bridge from table: {:?} has same IP and Port as bridge {:?}!", - reachable_bridge.0, bridge - ); // Now we must remove the old bridge from the table and insert the new bridge in its place // i.e., in the same bucket and with the same permissions. let positions = self.bridge_table.reachable.get(&reachable_bridge.0); if let Some(v) = positions { for (bucketnum, offset) in v.iter() { - println!("Bucket num: {:?} and offset: {:?}", bucketnum, offset); let mut bridgelines = match self.bridge_table.buckets.get(bucketnum) { Some(bridgelines) => *bridgelines, None => return res, @@ -445,11 +468,9 @@ impl BridgeAuth { assert!(bridgelines[*offset] == reachable_bridge.0); bridgelines[*offset] = *bridge; self.bridge_table.buckets.insert(*bucketnum, bridgelines); - let bridgelines = match self.bridge_table.buckets.get(bucketnum) { - Some(bridgelines) => *bridgelines, - None => return res, - }; - assert!(bridgelines[*offset] != reachable_bridge.0); + if self.bridge_table.buckets.get(bucketnum).is_none() { + return res; + } } res = true; } else { @@ -776,6 +797,11 @@ impl BridgeAuth { .iter() .partition(|&x| x.1 + EXPIRY_DATE < self.today()); for item in expired { + // We should check that the items were actually distributed before they are removed + if !bdb.distributed_buckets.contains(&item.0) { + // TODO: Add prometheus metric for this? + println!("This bucket was not actually distributed!"); + } let new_item = item.0; bdb.remove_blocked_or_expired_buckets(&new_item); // Remove any trust upgrade migrations from this diff --git a/crates/lox-library/src/migration_table.rs b/crates/lox-library/src/migration_table.rs index 5697b00..612e71e 100644 --- a/crates/lox-library/src/migration_table.rs +++ b/crates/lox-library/src/migration_table.rs @@ -16,8 +16,8 @@ use curve25519_dalek::scalar::Scalar; use sha2::Digest; use sha2::Sha256; -use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead}; -use aes_gcm::Aes128Gcm; +use aes_gcm::aead::{generic_array::GenericArray, Aead}; +use aes_gcm::{Aes128Gcm, KeyInit}; use rand::RngCore; use std::collections::HashMap; @@ -249,8 +249,12 @@ pub fn decrypt_cred( let mut to_bucket_bytes: [u8; 32] = [0; 32]; to_bucket_bytes.copy_from_slice(&plaintextbytes[..32]); let to_bucket = Scalar::from_bytes_mod_order(to_bucket_bytes); - let P = CompressedRistretto::from_slice(&plaintextbytes[32..64]).decompress()?; - let Q = CompressedRistretto::from_slice(&plaintextbytes[64..]).decompress()?; + let P = CompressedRistretto::from_slice(&plaintextbytes[32..64]) + .expect("Unable to extract P from bucket") + .decompress()?; + let Q = CompressedRistretto::from_slice(&plaintextbytes[64..]) + .expect("Unable to extract Q from bucket") + .decompress()?; Some(Migration { P, diff --git a/crates/lox-library/src/proto/blockage_migration.rs b/crates/lox-library/src/proto/blockage_migration.rs index f1808cd..c367dcf 100644 --- a/crates/lox-library/src/proto/blockage_migration.rs +++ b/crates/lox-library/src/proto/blockage_migration.rs @@ -26,9 +26,7 @@ and a new Lox credential to be issued: - trust_level: revealed to be 2 less than the trust_level above - level_since: today - invites_remaining: revealed to be LEVEL_INVITATIONS for the new trust - level [Actually, there's a bug in the zkp crate that's triggered when - a public value is 0 (the identity element of the Ristretto group), so - we treat this field as blinded, but the _server_ encrypts the value.] + level - blockages: blinded, but proved in ZK that it's one more than the blockages above @@ -104,11 +102,9 @@ pub struct Response { // The fields for the new Lox credential P: RistrettoPoint, EncQ: (RistrettoPoint, RistrettoPoint), - EncInvRemain: (RistrettoPoint, RistrettoPoint), id_server: Scalar, TId: RistrettoPoint, TBucket: RistrettoPoint, - TInvRemain: RistrettoPoint, TBlockages: RistrettoPoint, // The ZKP @@ -155,11 +151,10 @@ define_proof! { blindissue, "Blockage Migration Blind Issuing", (x0, x0tilde, xid, xbucket, xlevel, xsince, xinvremain, xblockages, - s, b, tid, tbucket, tinvremain, tblockages), + s, b, tid, tbucket, tblockages), (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xlevel, Xsince, Xinvremain, - Xblockages, Plevel, Psince, TId, TBucket, TInvRemain, TBlockages, - D, EncId0, EncId1, EncBucket0, EncBucket1, EncInvRemain0, - EncInvRemain1, EncBlockages0, EncBlockages1), + Xblockages, Plevel, Psince, Pinvremain, TId, TBucket, TBlockages, + D, EncId0, EncId1, EncBucket0, EncBucket1, EncBlockages0, EncBlockages1), (A, B): Xid = (xid*A), Xlevel = (xlevel*A), @@ -173,14 +168,12 @@ define_proof! { TId = (tid*A), TBucket = (b*Xbucket), TBucket = (tbucket*A), - TInvRemain = (b*Xinvremain), - TInvRemain = (tinvremain*A), TBlockages = (b*Xblockages), TBlockages = (tblockages*A), EncQ0 = (s*B + tid*EncId0 + tbucket*EncBucket0 - + tinvremain*EncInvRemain0 + tblockages*EncBlockages0), + + tblockages*EncBlockages0), EncQ1 = (s*D + tid*EncId1 + tbucket*EncBucket1 - + tinvremain*EncInvRemain1 + tblockages*EncBlockages1 + + tblockages*EncBlockages1 + x0*P + xlevel*Plevel + xsince*Psince) } @@ -288,7 +281,7 @@ pub fn request( &migration_cred.to_bucket * Btable + ebucket * D, ); let eblockages = Scalar::random(&mut rng); - let new_blockages = lox_cred.blockages + Scalar::one(); + let new_blockages = lox_cred.blockages + Scalar::ONE; let EncBlockages = ( &eblockages * Btable, &new_blockages * Btable + eblockages * D, @@ -484,14 +477,6 @@ impl BridgeAuth { // invitations for moving from level i to level i+1) let invremain: Scalar = LEVEL_INVITATIONS[(level - 3) as usize].into(); - // Because of the bug in the zkp crate, encrypt the invites - // remaining instead of sending it in the clear - let sinvremain = Scalar::random(&mut rng); - let EncInvRemain = ( - &sinvremain * Btable, - &invremain * Btable + sinvremain * req.D, - ); - // Compute the MAC on the visible attributes let b = Scalar::random(&mut rng); let P = &b * Btable; @@ -512,9 +497,6 @@ impl BridgeAuth { let tbucket = self.lox_priv.x[2] * b; let TBucket = &tbucket * Atable; let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1); - let tinvremain = self.lox_priv.x[5] * b; - let TInvRemain = &tinvremain * Atable; - let EncQInvRemain = (tinvremain * EncInvRemain.0, tinvremain * EncInvRemain.1); let tblockages = self.lox_priv.x[6] * b; let TBlockages = &tblockages * Atable; let EncQBlockages = ( @@ -523,8 +505,8 @@ impl BridgeAuth { ); let EncQ = ( - EncQHc.0 + EncQId.0 + EncQBucket.0 + EncQInvRemain.0 + EncQBlockages.0, - EncQHc.1 + EncQId.1 + EncQBucket.1 + EncQInvRemain.1 + EncQBlockages.1, + EncQHc.0 + EncQId.0 + EncQBucket.0 + EncQBlockages.0, + EncQHc.1 + EncQId.1 + EncQBucket.1 + EncQBlockages.1, ); let mut transcript = Transcript::new(b"blockage migration issuing"); @@ -545,17 +527,15 @@ impl BridgeAuth { Xblockages: &self.lox_pub.X[6], Plevel: &(trust_level * P), Psince: &(level_since * P), + Pinvremain: &(invremain * P), TId: &TId, TBucket: &TBucket, - TInvRemain: &TInvRemain, TBlockages: &TBlockages, D: &req.D, EncId0: &EncId.0, EncId1: &EncId.1, EncBucket0: &req.EncBucket.0, EncBucket1: &req.EncBucket.1, - EncInvRemain0: &EncInvRemain.0, - EncInvRemain1: &EncInvRemain.1, EncBlockages0: &req.EncBlockages.0, EncBlockages1: &req.EncBlockages.1, x0: &self.lox_priv.x[0], @@ -570,7 +550,6 @@ impl BridgeAuth { b: &b, tid: &tid, tbucket: &tbucket, - tinvremain: &tinvremain, tblockages: &tblockages, }, ) @@ -580,11 +559,9 @@ impl BridgeAuth { level_since, P, EncQ, - EncInvRemain, id_server, TId, TBucket, - TInvRemain, TBlockages, piBlindIssue, }) @@ -601,7 +578,6 @@ pub fn handle_response( let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE; - if resp.P.is_identity() { return Err(ProofError::VerificationFailure); } @@ -627,13 +603,6 @@ pub fn handle_response( // moving from level i to level i+1) let invremain: Scalar = LEVEL_INVITATIONS[(new_level - 1) as usize].into(); - // Decrypt EncInvRemain - let recv_invremain = resp.EncInvRemain.1 - (state.d * resp.EncInvRemain.0); - - if recv_invremain != &invremain * Btable { - return Err(ProofError::VerificationFailure); - } - // Verify the proof let mut transcript = Transcript::new(b"blockage migration issuing"); blindissue::verify_compact( @@ -654,17 +623,15 @@ pub fn handle_response( Xblockages: &lox_pub.X[6].compress(), Plevel: &(state.trust_level * resp.P).compress(), Psince: &(resp.level_since * resp.P).compress(), + Pinvremain: &(invremain * resp.P).compress(), TId: &resp.TId.compress(), TBucket: &resp.TBucket.compress(), - TInvRemain: &resp.TInvRemain.compress(), TBlockages: &resp.TBlockages.compress(), D: &state.D.compress(), EncId0: &EncId.0.compress(), EncId1: &EncId.1.compress(), EncBucket0: &state.EncBucket.0.compress(), EncBucket1: &state.EncBucket.1.compress(), - EncInvRemain0: &resp.EncInvRemain.0.compress(), - EncInvRemain1: &resp.EncInvRemain.1.compress(), EncBlockages0: &state.EncBlockages.0.compress(), EncBlockages1: &state.EncBlockages.1.compress(), }, diff --git a/crates/lox-library/src/proto/issue_invite.rs b/crates/lox-library/src/proto/issue_invite.rs index 0dfde3b..a512270 100644 --- a/crates/lox-library/src/proto/issue_invite.rs +++ b/crates/lox-library/src/proto/issue_invite.rs @@ -269,7 +269,7 @@ pub fn request( // Ensure the credential can be correctly shown: it must be the case // that invites_remaining not be 0 - if lox_cred.invites_remaining == Scalar::zero() { + if lox_cred.invites_remaining == Scalar::ZERO { return Err(ProofError::VerificationFailure); } // The buckets in the Lox and Bucket Reachability credentials have @@ -286,7 +286,7 @@ pub fn request( return Err(ProofError::VerificationFailure); } // The new invites_remaining - let new_invites_remaining = lox_cred.invites_remaining - Scalar::one(); + let new_invites_remaining = lox_cred.invites_remaining - Scalar::ONE; // Blind showing the Lox credential diff --git a/crates/lox-library/src/proto/migration.rs b/crates/lox-library/src/proto/migration.rs index 36ab7d0..ce7bc5f 100644 --- a/crates/lox-library/src/proto/migration.rs +++ b/crates/lox-library/src/proto/migration.rs @@ -168,7 +168,7 @@ pub fn request( // This protocol only allows migrating from trust level 0 to trust // level 1 - if lox_cred.trust_level != Scalar::zero() { + if lox_cred.trust_level != Scalar::ZERO { return Err(ProofError::VerificationFailure); } @@ -323,7 +323,7 @@ impl BridgeAuth { } // We only currently support migrating from trust level 0 - if req.trust_level != Scalar::zero() { + if req.trust_level != Scalar::ZERO { return Err(ProofError::VerificationFailure); } @@ -387,7 +387,7 @@ impl BridgeAuth { // Create the trust_level attrubute (Scalar), which will be // level 1 - let trust_level: Scalar = Scalar::one(); + let trust_level: Scalar = Scalar::ONE; // Create the level_since attribute (Scalar), which is today's // Julian date @@ -513,7 +513,7 @@ pub fn handle_response( Xlevel: &lox_pub.X[3].compress(), Xsince: &lox_pub.X[4].compress(), // The new trust level is 1 - Plevel: &(Scalar::one() * resp.P).compress(), + Plevel: &(Scalar::ONE * resp.P).compress(), Psince: &(resp.level_since * resp.P).compress(), TId: &resp.TId.compress(), TBucket: &resp.TBucket.compress(), @@ -533,9 +533,9 @@ pub fn handle_response( Q, id, bucket: state.to_bucket, - trust_level: Scalar::one(), + trust_level: Scalar::ONE, level_since: resp.level_since, - invites_remaining: Scalar::zero(), - blockages: Scalar::zero(), + invites_remaining: Scalar::ZERO, + blockages: Scalar::ZERO, }) } diff --git a/crates/lox-library/src/proto/open_invite.rs b/crates/lox-library/src/proto/open_invite.rs index 69960dd..b95fc4a 100644 --- a/crates/lox-library/src/proto/open_invite.rs +++ b/crates/lox-library/src/proto/open_invite.rs @@ -324,10 +324,10 @@ pub fn handle_response( Q, id, bucket: resp.bucket, - trust_level: Scalar::zero(), + trust_level: Scalar::ZERO, level_since: resp.level_since, - invites_remaining: Scalar::zero(), - blockages: Scalar::zero(), + invites_remaining: Scalar::ZERO, + blockages: Scalar::ZERO, }, resp.bridge_line, )) diff --git a/crates/lox-library/src/proto/redeem_invite.rs b/crates/lox-library/src/proto/redeem_invite.rs index d90fb1d..9f288d6 100644 --- a/crates/lox-library/src/proto/redeem_invite.rs +++ b/crates/lox-library/src/proto/redeem_invite.rs @@ -458,7 +458,7 @@ impl BridgeAuth { let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable); // The trust level for invitees is always 1 - let level = Scalar::one(); + let level = Scalar::ONE; // The invites remaining for invitees is always 0 (as // appropriate for trust level 1), so we don't need to actually @@ -611,9 +611,9 @@ pub fn handle_response( Q, id, bucket: state.bucket, - trust_level: Scalar::one(), + trust_level: Scalar::ONE, level_since: resp.level_since, - invites_remaining: Scalar::zero(), + invites_remaining: Scalar::ZERO, blockages: state.blockages, }) } diff --git a/crates/lox-library/src/tests.rs b/crates/lox-library/src/tests.rs index 428fcf1..3c47e84 100644 --- a/crates/lox-library/src/tests.rs +++ b/crates/lox-library/src/tests.rs @@ -64,7 +64,7 @@ impl TestHarness { fn open_invite(&mut self) -> (PerfStat, (cred::Lox, bridge_table::BridgeLine)) { // Issue an open invitation - let inv = self.bdb.invite(); + let inv = self.bdb.invite().unwrap(); let req_start = Instant::now(); // Use it to get a Lox credential @@ -394,17 +394,21 @@ fn test_open_invite() { assert!(bridgeline == bucket.0[0]); } - #[test] fn test_k_invites() { let mut th = TestHarness::new(); for i in 0..25 { let _ = th.open_invite(); - if (i+1) % OPENINV_K != 0 { - assert!(th.bdb.current_k == (i+1)%OPENINV_K, "the current_k should be (i+1)%OPENINV_K"); + if (i + 1) % OPENINV_K != 0 { + assert!( + th.bdb.current_k == (i + 1) % OPENINV_K, + "the current_k should be (i+1)%OPENINV_K" + ); } else { - assert!(th.bdb.current_k == OPENINV_K, "the current_k should be OPENINV_K"); - + assert!( + th.bdb.current_k == OPENINV_K, + "the current_k should be OPENINV_K" + ); } } } @@ -871,7 +875,7 @@ fn block_bridges(th: &mut TestHarness, to_block: usize) { let mut rng = rand::thread_rng(); while block_index.len() < to_block { - let rand_num = rng.gen_range(1, blockable_range); + let rand_num = rng.gen_range(1..blockable_range); if !th.bdb.openinv_buckets.contains(&(rand_num as u32)) && !th.bdb.distributed_buckets.contains(&(rand_num as u32)) && !block_index.contains(&rand_num) @@ -1041,7 +1045,7 @@ fn test_bridge_replace() { let table_size = th.ba.bridge_table.buckets.len(); let mut num = 100000; while !th.ba.bridge_table.buckets.contains_key(&num) { - num = rand::thread_rng().gen_range(0, th.ba.bridge_table.counter); + num = rand::thread_rng().gen_range(0..th.ba.bridge_table.counter); } let replaceable_bucket = *th.ba.bridge_table.buckets.get(&num).unwrap(); let replacement_bridge = &replaceable_bucket[0]; diff --git a/crates/lox-library/tests/tests.rs b/crates/lox-library/tests/tests.rs index a270194..9bc376f 100644 --- a/crates/lox-library/tests/tests.rs +++ b/crates/lox-library/tests/tests.rs @@ -10,7 +10,7 @@ fn test_bridgedb() { for i in &[1u32, 5, 7, 12, 19, 20, 22] { bdb.insert_openinv(*i); } - let inv = bdb.invite(); + let inv = bdb.invite().unwrap(); println!("{:?}", inv); let res = BridgeDb::verify(inv, bdb.pubkey); println!("{:?}", res); diff --git a/crates/lox-utils/Cargo.toml b/crates/lox-utils/Cargo.toml index 0ba29b8..c91fe99 100644 --- a/crates/lox-utils/Cargo.toml +++ b/crates/lox-utils/Cargo.toml @@ -14,8 +14,8 @@ repository = "https://gitlab.torproject.org/tpo/anti-censorship/lox.git/" [dependencies] lox-library = {path = "../lox-library", version = "0.1.0"} serde = "1" -serde_json = "1.0.105" -serde_with = "3.3.0" +serde_json = "1.0.108" +serde_with = "3.4.0" [features] diff --git a/crates/lox-wasm/Cargo.toml b/crates/lox-wasm/Cargo.toml index 01c14c7..3a5ccfd 100644 --- a/crates/lox-wasm/Cargo.toml +++ b/crates/lox-wasm/Cargo.toml @@ -10,19 +10,20 @@ license = "MIT" crate-type = ["cdylib"] [dependencies] +getrandom = { version = "0.2", features = ["js"] } julianday = "1.2.0" lazy_static = "1.4.0" lox-library = { path = "../lox-library", version = "0.1.0" } lox_utils = { path = "../lox-utils", version = "0.1.0" } wasm-bindgen = "0.2" -time = "0.3.28" -serde_json = "1.0.105" +time = "0.3.30" +serde_json = "1.0.108" console_error_panic_hook = "0.1.7" -js-sys = "0.3.64" +js-sys = "0.3.65" rand = { version = "0.7", features = ["wasm-bindgen"] } -zkp = "0.8.0" +zkp = { git = "https://gitlab.torproject.org/onyinyang/lox-zkp" } [dependencies.chrono] -version = "0.4.27" +version = "0.4.31" features = ["serde", "wasmbind"] diff --git a/crates/rdsys-backend-api/Cargo.toml b/crates/rdsys-backend-api/Cargo.toml index 853ff15..e55a241 100644 --- a/crates/rdsys-backend-api/Cargo.toml +++ b/crates/rdsys-backend-api/Cargo.toml @@ -13,10 +13,10 @@ serde = { version = "1", features = ["derive"]} bytes = "1" hex = "0.4.3" crc64 = "2.0.0" -sha1 = "0.10.5" -tokio = { version = "1", features = ["macros"]} -reqwest = { version = "0.11", features = ["stream"]} +sha1 = "0.10.6" +tokio = { version = "1", features = ["full", "macros"] } +reqwest = { version = "0.11", features = ["json", "stream"]} tokio-stream = "0.1.14" -futures = "0.3.28" -tokio-util = "0.7.8" -chrono = { version = "0.4.27", features = ["serde", "clock"] } +futures = "0.3.29" +tokio-util = "0.7.10" +chrono = { version = "0.4.31", features = ["serde", "clock"] } diff --git a/crates/rdsys-backend-api/src/lib.rs b/crates/rdsys-backend-api/src/lib.rs index 84b420a..51e2bdc 100644 --- a/crates/rdsys-backend-api/src/lib.rs +++ b/crates/rdsys-backend-api/src/lib.rs @@ -6,7 +6,7 @@ use bytes::{self, Buf, Bytes}; use core::pin::Pin; use futures_util::{Stream, StreamExt}; -use reqwest::Client; +use reqwest::{Client, StatusCode}; use std::io::{self, BufRead}; use std::task::{ready, Context, Poll}; use tokio::sync::mpsc; @@ -19,6 +19,7 @@ pub enum Error { Reqwest(reqwest::Error), Io(io::Error), JSON(serde_json::Error), + String(StatusCode), } impl From for Error { @@ -220,7 +221,7 @@ mod tests { /// } /// } /// ``` -/// + pub async fn start_stream( api_endpoint: String, name: String, @@ -260,3 +261,39 @@ pub async fn start_stream( }); Ok(ResourceStream::new(rx)) } + +pub async fn request_resources( + api_endpoint: String, + name: String, + token: String, + resource_types: Vec, +) -> Result { + let fetched_resources: Result; + let req = proto::ResourceRequest { + request_origin: name, + resource_types, + }; + let json = serde_json::to_string(&req)?; + + let auth_value = format!("Bearer {}", token); + + let client = Client::new(); + + let response = client + .get(api_endpoint) + .header("Authorization", &auth_value) + .body(json) + .send() + .await + .unwrap(); + match response.status() { + reqwest::StatusCode::OK => { + fetched_resources = match response.json::().await { + Ok(fetched_resources) => Ok(fetched_resources), + Err(e) => Err(Error::Reqwest(e)), + }; + } + other => fetched_resources = Err(Error::String(other)), + }; + fetched_resources +} diff --git a/crates/rdsys-backend-api/src/proto.rs b/crates/rdsys-backend-api/src/proto.rs index af0a026..7ce41e9 100644 --- a/crates/rdsys-backend-api/src/proto.rs +++ b/crates/rdsys-backend-api/src/proto.rs @@ -10,12 +10,17 @@ pub struct ResourceRequest { pub resource_types: Vec, } +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct TestResults { + pub last_passed: DateTime, +} + /// Representation of a bridge resource #[derive(Deserialize, PartialEq, Eq, Debug)] pub struct Resource { pub r#type: String, pub blocked_in: HashMap, - pub last_passed: DateTime, + pub test_result: TestResults, pub protocol: String, pub address: String, pub port: u16, @@ -51,6 +56,13 @@ impl Resource { } } +/// A ResourceState holds information about new, changed, or pruned resources +#[derive(Deserialize, PartialEq, Eq, Debug)] +pub struct ResourceState { + pub working: Option>, + pub not_working: Option>, +} + /// A ResourceDiff holds information about new, changed, or pruned resources #[derive(Deserialize, PartialEq, Eq, Debug)] pub struct ResourceDiff { @@ -92,7 +104,9 @@ mod tests { let bridge = Resource { r#type: String::from("scramblesuit"), blocked_in: HashMap::new(), - last_passed: "2023-05-30T14:20:28Z".parse::>().unwrap(), + test_result: TestResults { + last_passed: "2023-05-30T14:20:28Z".parse::>().unwrap(), + }, protocol: String::from("tcp"), address: String::from("216.117.3.62"), port: 63174, @@ -107,7 +121,9 @@ mod tests { { "type": "scramblesuit", "blocked_in": {}, - "last_passed": "2023-05-30T14:20:28.000+00:00", + "test_result" : { + "last_passed": "2023-05-30T14:20:28.000+00:00" + }, "protocol": "tcp", "address": "216.117.3.62", "port": 63174, @@ -135,7 +151,9 @@ mod tests { { "type": "obfs2", "blocked_in": {}, - "last_passed": "2023-05-30T11:42:28.000+07:00", + "test_result" : { + "last_passed": "2023-05-30T11:42:28.000+07:00" + }, "Location": null, "protocol": "tcp", "address": "176.247.216.207", @@ -153,7 +171,9 @@ mod tests { { "type": "obfs2", "blocked_in": {}, - "last_passed": "2023-05-30T12:20:28.000+07:00", + "test_result" : { + "last_passed": "2023-05-30T12:20:28.000+07:00" + }, "protocol": "tcp", "address": "133.69.16.145", "port": 58314, @@ -172,7 +192,9 @@ mod tests { { "type": "scramblesuit", "blocked_in": {}, - "last_passed": "2023-05-30T14:20:28.000+07:00", + "test_result" : { + "last_passed": "2023-05-30T14:20:28.000+07:00" + }, "protocol": "tcp", "address": "216.117.3.62", "port": 63174,