From 761c5019f0b7e707f970d8923dee8aa4270e81f4 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 29 Jul 2024 13:44:36 +0300 Subject: [PATCH 01/23] add rust-analyzer to the workspace Signed-off-by: onur-ozkan --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fd74a04..c86325a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -components = [ "rustfmt", "clippy" ] +components = [ "rustfmt", "clippy", "rust-analyzer"] channel = "1.75" From c037af295afb79b9807bb4bcd568b5b7500e7092 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 29 Jul 2024 14:00:55 +0300 Subject: [PATCH 02/23] use the latest upstream checkout Signed-off-by: onur-ozkan --- Cargo.lock | 23 ++++++++++++++--------- Cargo.toml | 4 ++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d86739..29b859b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,10 +45,16 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bitcoin_hashes" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" + [[package]] name = "bitcrypto" version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/atomicDEX-API?branch=dev#3be0c0449045ef0ffbf4f4dadb714368eb8fb74b" +source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" dependencies = [ "groestl", "primitives", @@ -915,8 +921,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primitives" version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/atomicDEX-API?branch=dev#3be0c0449045ef0ffbf4f4dadb714368eb8fb74b" +source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" dependencies = [ + "bitcoin_hashes", "byteorder", "rustc-hex 2.1.0", "uint 0.9.3", @@ -1280,7 +1287,7 @@ dependencies = [ [[package]] name = "serialization" version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/atomicDEX-API?branch=dev#3be0c0449045ef0ffbf4f4dadb714368eb8fb74b" +source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" dependencies = [ "byteorder", "derive_more", @@ -1314,15 +1321,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "digest 0.10.7", ] [[package]] @@ -1435,7 +1440,7 @@ dependencies = [ [[package]] name = "test_helpers" version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/atomicDEX-API?branch=dev#3be0c0449045ef0ffbf4f4dadb714368eb8fb74b" +source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" dependencies = [ "hex", ] diff --git a/Cargo.toml b/Cargo.toml index c9ff2d2..c29646c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,9 @@ simple_logger = "2.1.0" tokio = { version = "1.12.0", default-features = false, features = ["macros", "rt-multi-thread", "sync", "time"] } tokio-tungstenite = { version = "0.20.0", features = ["native-tls"] } # From our sources -bitcrypto = { git = "https://github.com/KomodoPlatform/atomicDEX-API", branch = "dev" } +bitcrypto = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", branch = "cosmos-kdp-impl" } ethkey = { git = "https://github.com/artemii235/parity-ethereum.git" } -serialization = { git = "https://github.com/KomodoPlatform/atomicDEX-API", branch = "dev" } +serialization = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", branch = "cosmos-kdp-impl" } url = { version = "2.2.2", features = ["serde"] } [target.x86_64-unknown-linux-gnu.dependencies] From 272a9b71a51c68beefb8461cbb480029ffc16f67 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 29 Jul 2024 16:28:47 +0300 Subject: [PATCH 03/23] add `proxy_signature` crate from kdf Signed-off-by: onur-ozkan --- Cargo.lock | 1016 +++++++++++++++++++++++++++++++++++++++++++--------- Cargo.toml | 3 +- 2 files changed, 846 insertions(+), 173 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29b859b..7d084ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,39 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arrayref" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" + [[package]] name = "async-trait" version = "0.1.56" @@ -13,6 +46,19 @@ dependencies = [ "syn 1.0.96", ] +[[package]] +name = "asynchronous-codec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + [[package]] name = "atty" version = "0.2.14" @@ -39,6 +85,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base64" version = "0.13.0" @@ -46,25 +98,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] -name = "bitcoin_hashes" -version = "0.11.0" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" - -[[package]] -name = "bitcrypto" -version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" -dependencies = [ - "groestl", - "primitives", - "ripemd160", - "serialization", - "sha-1", - "sha2", - "sha3", - "siphasher", -] +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" @@ -72,6 +109,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "block-buffer" version = "0.9.0" @@ -97,6 +140,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.10.0" @@ -117,9 +169,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "cc" -version = "1.0.73" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" [[package]] name = "cfg-if" @@ -127,13 +179,27 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -162,10 +228,10 @@ dependencies = [ ] [[package]] -name = "convert_case" -version = "0.4.0" +name = "const-oid" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "core-foundation" @@ -183,11 +249,20 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -214,6 +289,33 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + [[package]] name = "data-encoding" version = "2.5.0" @@ -221,18 +323,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] -name = "derive_more" -version = "0.99.17" +name = "data-encoding-macro" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" +dependencies = [ + "data-encoding", "syn 1.0.96", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "digest" version = "0.9.0" @@ -250,6 +369,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", + "subtle", ] [[package]] @@ -258,12 +378,48 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "edit-distance" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "ethbloom" version = "0.5.3" @@ -288,7 +444,7 @@ dependencies = [ "ethereum-types-serialize", "fixed-hash", "serde", - "uint 0.4.1", + "uint", ] [[package]] @@ -327,6 +483,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "fixed-hash" version = "0.2.5" @@ -380,13 +542,29 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -395,6 +573,24 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + [[package]] name = "futures-macro" version = "0.3.30" @@ -418,16 +614,25 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -454,17 +659,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "groestl" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2432787a9b8f0d58dca43fe2240399479b7582dc8afa2126dc7652b864029e47" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "h2" version = "0.3.13" @@ -490,6 +684,15 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -505,6 +708,24 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "http" version = "0.2.8" @@ -529,9 +750,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.7.1" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -576,6 +797,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.2.3" @@ -594,7 +838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" dependencies = [ "autocfg 1.1.0", - "hashbrown", + "hashbrown 0.11.2", ] [[package]] @@ -680,22 +924,180 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "libp2p" +version = "0.52.1" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "getrandom", + "instant", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core", + "libp2p-identify", + "libp2p-identity", + "libp2p-metrics", + "libp2p-swarm", + "multiaddr", + "pin-project", +] + +[[package]] +name = "libp2p-allow-block-list" +version = "0.2.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.2.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-core" +version = "0.40.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "log 0.4.22", + "multiaddr", + "multihash", + "multistream-select", + "once_cell", + "parking_lot", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink", + "smallvec", + "thiserror", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-identify" +version = "0.43.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "asynchronous-codec", + "either", + "futures", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log 0.4.22", + "lru", + "quick-protobuf", + "quick-protobuf-codec", + "smallvec", + "thiserror", + "void", +] + +[[package]] +name = "libp2p-identity" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" +dependencies = [ + "bs58", + "ed25519-dalek", + "hkdf", + "multihash", + "quick-protobuf", + "sha2", + "thiserror", + "tracing", + "zeroize", +] + +[[package]] +name = "libp2p-metrics" +version = "0.13.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "instant", + "libp2p-core", + "libp2p-identify", + "libp2p-identity", + "libp2p-swarm", + "once_cell", + "prometheus-client", +] + +[[package]] +name = "libp2p-swarm" +version = "0.43.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "log 0.4.22", + "multistream-select", + "once_cell", + "rand 0.8.5", + "smallvec", + "void", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg 1.1.0", + "scopeguard", +] + [[package]] name = "log" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" dependencies = [ - "log 0.4.17", + "log 0.4.22", ] [[package]] name = "log" -version = "0.4.17" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ - "cfg-if", + "hashbrown 0.13.2", ] [[package]] @@ -703,7 +1105,6 @@ name = "main" version = "0.1.0" dependencies = [ "async-trait", - "bitcrypto", "bytes", "ethereum-types", "ethkey", @@ -713,12 +1114,12 @@ dependencies = [ "hyper-tls", "jemallocator", "jsonwebtoken", - "log 0.4.17", + "log 0.4.22", "once_cell", + "proxy_signature", "redis", "serde", "serde_json", - "serialization", "sha3", "simple_logger", "tokio", @@ -727,32 +1128,85 @@ dependencies = [ ] [[package]] -name = "matches" -version = "0.1.9" +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "mem" +version = "0.1.0" +source = "git+https://github.com/artemii235/parity-ethereum.git#0a090f9b3efd7e24193265cf0e4109bf2369ad98" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +dependencies = [ + "libc", + "log 0.4.22", + "wasi", + "windows-sys", +] + +[[package]] +name = "multiaddr" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b852bc02a2da5feed68cd14fa50d0774b92790a5bdbfa932a813926c8472070" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "mem" -version = "0.1.0" -source = "git+https://github.com/artemii235/parity-ethereum.git#0a090f9b3efd7e24193265cf0e4109bf2369ad98" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] [[package]] -name = "memchr" -version = "2.5.0" +name = "multihash" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +dependencies = [ + "core2", + "unsigned-varint", +] [[package]] -name = "mio" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +name = "multistream-select" +version = "0.13.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" dependencies = [ - "libc", - "log 0.4.17", - "wasi", - "windows-sys", + "bytes", + "futures", + "log 0.4.22", + "pin-project", + "smallvec", + "unsigned-varint", ] [[package]] @@ -763,7 +1217,7 @@ checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", - "log 0.4.17", + "log 0.4.22", "openssl", "openssl-probe", "openssl-sys", @@ -824,9 +1278,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -840,7 +1294,7 @@ version = "0.10.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -879,6 +1333,29 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.3", + "smallvec", + "windows-targets", +] + [[package]] name = "pem" version = "1.0.2" @@ -894,6 +1371,26 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -906,6 +1403,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.25" @@ -919,23 +1426,68 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "primitives" +name = "proc-macro2" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus-client" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c99afa9a01501019ac3a14d71d9f94050346f55ca471ce90c799a15c58f61e2" +dependencies = [ + "dtoa 1.0.9", + "itoa 1.0.2", + "parking_lot", + "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 2.0.43", +] + +[[package]] +name = "proxy_signature" version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" +source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?rev=0784f1a#0784f1a3ce03f608caca463d209827c23b485b55" dependencies = [ - "bitcoin_hashes", - "byteorder", - "rustc-hex 2.1.0", - "uint 0.9.3", + "chrono", + "http", + "libp2p", + "serde", + "serde_json", ] [[package]] -name = "proc-macro2" -version = "1.0.71" +name = "quick-protobuf" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" dependencies = [ - "unicode-ident", + "byteorder", +] + +[[package]] +name = "quick-protobuf-codec" +version = "0.2.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror", + "unsigned-varint", ] [[package]] @@ -1114,7 +1666,7 @@ dependencies = [ "async-trait", "bytes", "combine", - "dtoa", + "dtoa 0.4.8", "futures-util", "itoa 0.4.8", "percent-encoding", @@ -1130,7 +1682,16 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", ] [[package]] @@ -1157,17 +1718,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "ripemd160" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "rustc-hex" version = "1.0.0" @@ -1189,6 +1739,16 @@ dependencies = [ "semver", ] +[[package]] +name = "rw-stream-sink" +version = "0.4.0" +source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc061b58853c1b0dafaa19a4a29343c0ac6eab3" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + [[package]] name = "ryu" version = "1.0.10" @@ -1205,6 +1765,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "secp256k1" version = "0.20.3" @@ -1230,7 +1796,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1279,35 +1845,12 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ + "indexmap", "itoa 1.0.2", "ryu", "serde", ] -[[package]] -name = "serialization" -version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" -dependencies = [ - "byteorder", - "derive_more", - "primitives", - "test_helpers", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha1" version = "0.10.6" @@ -1342,6 +1885,15 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "simple_asn1" version = "0.6.2" @@ -1362,23 +1914,23 @@ checksum = "c75a9723083573ace81ad0cdfc50b858aa3c366c48636edb4109d73122a0c0ea" dependencies = [ "atty", "colored", - "log 0.4.17", + "log 0.4.22", "time", "winapi", ] -[[package]] -name = "siphasher" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833011ca526bd88f16778d32c699d325a9ad302fa06381cd66f7be63351d3f6d" - [[package]] name = "slab" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "socket2" version = "0.4.4" @@ -1395,12 +1947,28 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.96" @@ -1432,19 +2000,11 @@ dependencies = [ "cfg-if", "fastrand", "libc", - "redox_syscall", + "redox_syscall 0.2.13", "remove_dir_all", "winapi", ] -[[package]] -name = "test_helpers" -version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?branch=cosmos-kdp-impl#4c0e2da084f42cec9d1baad907bd7b0132b352b8" -dependencies = [ - "hex", -] - [[package]] name = "thiserror" version = "1.0.31" @@ -1553,7 +2113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", - "log 0.4.17", + "log 0.4.22", "native-tls", "tokio", "tokio-native-tls", @@ -1569,7 +2129,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "log 0.4.17", + "log 0.4.22", "pin-project-lite", "tokio", ] @@ -1596,20 +2156,31 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.35" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] [[package]] -name = "tracing-core" +name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -1631,7 +2202,7 @@ dependencies = [ "data-encoding", "http", "httparse", - "log 0.4.17", + "log 0.4.22", "native-tls", "rand 0.8.5", "sha1", @@ -1657,18 +2228,6 @@ dependencies = [ "rustc-hex 2.1.0", ] -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy 0.2.2", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.8" @@ -1690,6 +2249,16 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" +dependencies = [ + "asynchronous-codec", + "bytes", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -1727,13 +2296,19 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "want" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log 0.4.17", + "log 0.4.22", "try-lock", ] @@ -1761,7 +2336,7 @@ checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" dependencies = [ "bumpalo", "lazy_static", - "log 0.4.17", + "log 0.4.22", "proc-macro2", "quote", "syn 1.0.96", @@ -1829,45 +2404,144 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index c29646c..5f5423b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,10 +26,9 @@ simple_logger = "2.1.0" tokio = { version = "1.12.0", default-features = false, features = ["macros", "rt-multi-thread", "sync", "time"] } tokio-tungstenite = { version = "0.20.0", features = ["native-tls"] } # From our sources -bitcrypto = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", branch = "cosmos-kdp-impl" } ethkey = { git = "https://github.com/artemii235/parity-ethereum.git" } -serialization = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", branch = "cosmos-kdp-impl" } url = { version = "2.2.2", features = ["serde"] } +proxy_signature = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", rev = "0784f1a" } [target.x86_64-unknown-linux-gnu.dependencies] jemallocator = "0.5.0" From 169438cd74c69161a6ae17790c748ffd86bc29a9 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 30 Jul 2024 08:15:39 +0300 Subject: [PATCH 04/23] WIP Signed-off-by: onur-ozkan --- Cargo.lock | 218 +++------------------------ Cargo.toml | 6 +- src/main.rs | 2 - src/proxy/quicknode.rs | 50 +++---- src/security/sign.rs | 326 ----------------------------------------- 5 files changed, 50 insertions(+), 552 deletions(-) delete mode 100644 src/security/sign.rs diff --git a/Cargo.lock b/Cargo.lock index 7d084ff..c3d192a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,15 +70,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -193,15 +184,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "colored" version = "2.0.0" @@ -408,12 +390,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "edit-distance" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b" - [[package]] name = "either" version = "1.13.0" @@ -456,24 +432,6 @@ dependencies = [ "serde", ] -[[package]] -name = "ethkey" -version = "0.3.0" -source = "git+https://github.com/artemii235/parity-ethereum.git#0a090f9b3efd7e24193265cf0e4109bf2369ad98" -dependencies = [ - "byteorder", - "edit-distance", - "ethereum-types", - "log 0.3.9", - "mem", - "rand 0.6.5", - "rustc-hex 1.0.0", - "secp256k1", - "serde", - "serde_derive", - "tiny-keccak", -] - [[package]] name = "fastrand" version = "1.7.0" @@ -496,7 +454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7afe6ce860afb14422711595a7b26ada9ed7de2f43c0b2ab79d09ee196287273" dependencies = [ "rand 0.4.6", - "rustc-hex 2.1.0", + "rustc-hex", ] [[package]] @@ -837,7 +795,7 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown 0.11.2", ] @@ -978,7 +936,7 @@ dependencies = [ "futures-timer", "instant", "libp2p-identity", - "log 0.4.22", + "log", "multiaddr", "multihash", "multistream-select", @@ -1006,7 +964,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "log 0.4.22", + "log", "lru", "quick-protobuf", "quick-protobuf-codec", @@ -1058,7 +1016,7 @@ dependencies = [ "instant", "libp2p-core", "libp2p-identity", - "log 0.4.22", + "log", "multistream-select", "once_cell", "rand 0.8.5", @@ -1072,19 +1030,10 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.22", -] - [[package]] name = "log" version = "0.4.22" @@ -1107,14 +1056,13 @@ dependencies = [ "async-trait", "bytes", "ethereum-types", - "ethkey", "futures-util", "hex", "hyper", "hyper-tls", "jemallocator", "jsonwebtoken", - "log 0.4.22", + "log", "once_cell", "proxy_signature", "redis", @@ -1133,11 +1081,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" -[[package]] -name = "mem" -version = "0.1.0" -source = "git+https://github.com/artemii235/parity-ethereum.git#0a090f9b3efd7e24193265cf0e4109bf2369ad98" - [[package]] name = "memchr" version = "2.5.0" @@ -1151,7 +1094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ "libc", - "log 0.4.22", + "log", "wasi", "windows-sys", ] @@ -1203,7 +1146,7 @@ source = "git+https://github.com/KomodoPlatform/rust-libp2p.git?tag=k-0.52.4#6fc dependencies = [ "bytes", "futures", - "log 0.4.22", + "log", "pin-project", "smallvec", "unsigned-varint", @@ -1217,7 +1160,7 @@ checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", - "log 0.4.22", + "log", "openssl", "openssl-probe", "openssl-sys", @@ -1233,7 +1176,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -1244,7 +1187,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -1254,7 +1197,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -1326,7 +1269,7 @@ version = "0.9.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cc", "libc", "pkg-config", @@ -1460,7 +1403,6 @@ dependencies = [ [[package]] name = "proxy_signature" version = "0.1.0" -source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?rev=0784f1a#0784f1a3ce03f608caca463d209827c23b485b55" dependencies = [ "chrono", "http", @@ -1512,25 +1454,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1538,20 +1461,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", + "rand_chacha", "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - [[package]] name = "rand_chacha" version = "0.3.1" @@ -1586,68 +1499,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rdrand" version = "0.4.0" @@ -1718,12 +1569,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rustc-hex" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" - [[package]] name = "rustc-hex" version = "2.1.0" @@ -1771,25 +1616,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "secp256k1" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" -dependencies = [ - "rand 0.6.5", - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" -dependencies = [ - "cc", -] - [[package]] name = "security-framework" version = "2.6.1" @@ -1914,7 +1740,7 @@ checksum = "c75a9723083573ace81ad0cdfc50b858aa3c366c48636edb4109d73122a0c0ea" dependencies = [ "atty", "colored", - "log 0.4.22", + "log", "time", "winapi", ] @@ -2113,7 +1939,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", - "log 0.4.22", + "log", "native-tls", "tokio", "tokio-native-tls", @@ -2129,7 +1955,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "log 0.4.22", + "log", "pin-project-lite", "tokio", ] @@ -2202,7 +2028,7 @@ dependencies = [ "data-encoding", "http", "httparse", - "log 0.4.22", + "log", "native-tls", "rand 0.8.5", "sha1", @@ -2225,7 +2051,7 @@ checksum = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" dependencies = [ "byteorder", "crunchy 0.1.6", - "rustc-hex 2.1.0", + "rustc-hex", ] [[package]] @@ -2308,7 +2134,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log 0.4.22", + "log", "try-lock", ] @@ -2336,7 +2162,7 @@ checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" dependencies = [ "bumpalo", "lazy_static", - "log 0.4.22", + "log", "proc-macro2", "quote", "syn 1.0.96", diff --git a/Cargo.toml b/Cargo.toml index 5f5423b..a6e2776 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ hyper-tls = "0.5.0" jsonwebtoken = "8.1.0" log = "0.4.17" once_cell = "1.12.0" +url = { version = "2.2.2", features = ["serde"] } redis = { version = "0.21.5", default-features = false, features = ["tokio-comp"] } serde = "1.0.137" serde_json = "1.0.81" @@ -26,9 +27,8 @@ simple_logger = "2.1.0" tokio = { version = "1.12.0", default-features = false, features = ["macros", "rt-multi-thread", "sync", "time"] } tokio-tungstenite = { version = "0.20.0", features = ["native-tls"] } # From our sources -ethkey = { git = "https://github.com/artemii235/parity-ethereum.git" } -url = { version = "2.2.2", features = ["serde"] } -proxy_signature = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", rev = "0784f1a" } +# proxy_signature = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", rev = "0784f1a" } +proxy_signature = { path = "/home/nimda/devspace/KomodoPlatform/komodo-defi-framework/mm2src/proxy_signature" } [target.x86_64-unknown-linux-gnu.dependencies] jemallocator = "0.5.0" diff --git a/src/main.rs b/src/main.rs index 2db1909..f4f041c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,6 @@ mod rate_limiter; mod rpc; #[path = "net/server.rs"] mod server; -#[path = "security/sign.rs"] -mod sign; #[path = "net/websocket.rs"] mod websocket; diff --git a/src/proxy/quicknode.rs b/src/proxy/quicknode.rs index 8945118..a886515 100644 --- a/src/proxy/quicknode.rs +++ b/src/proxy/quicknode.rs @@ -7,11 +7,11 @@ use crate::http::{ use crate::proxy::remove_hop_by_hop_headers; use crate::rate_limiter::RateLimitOperations; use crate::rpc::Json; -use crate::sign::{SignOps, SignedMessage}; use crate::{log_format, rpc, GenericResult}; use hyper::header::{HeaderName, HeaderValue}; use hyper::{header, Body, Request, Response, StatusCode, Uri}; use hyper_tls::HttpsConnector; +use proxy_signature::ProxySign; use serde::{Deserialize, Serialize}; use serde_json::json; use std::net::SocketAddr; @@ -34,11 +34,11 @@ pub(crate) struct QuicknodeSocketPayload { pub(crate) params: serde_json::value::Value, pub(crate) id: usize, pub(crate) jsonrpc: String, - pub(crate) signed_message: SignedMessage, + pub(crate) signed_message: ProxySign, } impl QuicknodeSocketPayload { - pub(crate) fn into_parts(self) -> (QuicknodePayload, SignedMessage) { + pub(crate) fn into_parts(self) -> (QuicknodePayload, ProxySign) { let payload = QuicknodePayload { method: self.method, params: self.params, @@ -64,7 +64,7 @@ pub(crate) async fn proxy_quicknode( mut req: Request, remote_addr: &SocketAddr, payload: QuicknodePayload, - signed_message: SignedMessage, + signed_message: ProxySign, x_forwarded_for: HeaderValue, proxy_route: &ProxyRoute, ) -> GenericResult> { @@ -74,7 +74,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", req.uri(), "Method {} not allowed for, returning 403.", payload.method @@ -93,7 +93,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", req.uri(), "Error inserting JWT into http header, returning 500." ) @@ -110,7 +110,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", original_req_uri, "Error type casting value of {} into Uri: {}, returning 500.", proxy_route.outbound_route, @@ -139,7 +139,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", original_req_uri, "Couldn't reach {}: {}. Returning 503.", target_uri, @@ -155,14 +155,14 @@ pub(crate) async fn proxy_quicknode( pub(crate) async fn validation_middleware_quicknode( cfg: &AppConfig, - signed_message: &SignedMessage, + signed_message: &ProxySign, proxy_route: &ProxyRoute, req_uri: &Uri, remote_addr: &SocketAddr, ) -> Result<(), StatusCode> { let mut db = Db::create_instance(cfg).await; - match db.read_address_status(&signed_message.address).await { + match db.read_address_status("ADDRESS_TODO").await { AddressStatus::Trusted => Ok(()), AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), AddressStatus::None => { @@ -174,7 +174,7 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", req_uri, "Request has invalid signed message, returning 401" ) @@ -184,7 +184,7 @@ pub(crate) async fn validation_middleware_quicknode( }; let rate_limiter_key = - format!("{}:{}", signed_message.coin_ticker, signed_message.address); + format!("{}:{}", "TICKER_TODO", "ADDRESS_TODO"); let rate_limiter = proxy_route .rate_limiter @@ -197,11 +197,11 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", req_uri, "Rate exceed for {}, checking balance for {} address.", rate_limiter_key, - signed_message.address + "ADDRESS_TODO" ) ); @@ -212,11 +212,11 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", req_uri, "Wallet {} has insufficient balance for coin {}, returning 406.", - signed_message.address, - signed_message.coin_ticker, + "ADDRESS_TODO", + "TICKER_TODO", ) ); @@ -227,10 +227,10 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", req_uri, "verify_message_and_balance failed in coin {}: {:?}", - signed_message.coin_ticker, + "TICKER_TODO", e ) ); @@ -245,7 +245,7 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - signed_message.address, + "ADDRESS_TODO", req_uri, "Rate incrementing failed." ) @@ -259,14 +259,14 @@ pub(crate) async fn validation_middleware_quicknode( async fn verify_message_and_balance( cfg: &AppConfig, - signed_message: &SignedMessage, + signed_message: &ProxySign, proxy_route: &ProxyRoute, ) -> Result<(), ProofOfFundingError> { - if let Ok(true) = signed_message.verify_message() { + if let true = signed_message.is_valid_message() { let mut db = Db::create_instance(cfg).await; // We don't want to send balance requests everytime when user sends requests. - if let Ok(true) = db.key_exists(&signed_message.address).await { + if let Ok(true) = db.key_exists("ADDRESS_TODO").await { return Ok(()); } @@ -274,7 +274,7 @@ async fn verify_message_and_balance( "id": 1, "jsonrpc": "2.0", "method": "eth_getBalance", - "params": [signed_message.address, "latest"] + "params": ["ADDRESS_TODO", "latest"] }); let rpc_client = @@ -287,7 +287,7 @@ async fn verify_message_and_balance( { Ok(res) if res["result"] != Json::Null && res["result"] != "0x0" => { // cache this address for 60 seconds - let _ = db.insert_cache(&signed_message.address, "", 60).await; + let _ = db.insert_cache("ADDRESS_TODO", "", 60).await; return Ok(()); } diff --git a/src/security/sign.rs b/src/security/sign.rs deleted file mode 100644 index 33a0945..0000000 --- a/src/security/sign.rs +++ /dev/null @@ -1,326 +0,0 @@ -use core::{convert::From, str::FromStr}; -use std::time::{SystemTime, UNIX_EPOCH}; - -use bitcrypto::keccak256; -use ethereum_types::{Address, H256}; -use ethkey::{sign, verify_address, Secret, Signature}; -use serde::{Deserialize, Serialize}; -use serialization::{CompactInteger, Serializable, Stream}; -use sha3::{Digest, Keccak256}; - -use super::*; - -pub(crate) trait SignOps { - fn sign_message_hash(&self) -> [u8; 32]; - fn checksum_address(&self) -> String; - fn is_valid_checksum_addr(&self) -> bool; - fn valid_addr_from_str(&self) -> Result; - fn addr_from_str(&self) -> Result; - #[allow(dead_code)] - fn sign_message(&mut self, secret: &Secret) -> GenericResult<()>; - fn verify_message(&self) -> GenericResult; -} - -/// Represents a signed message used for authenticating and validating requests processed by the proxy. -/// -/// This structure contains cryptographic elements (`signature`) and metadata (`coin_ticker`, `address`, `timestamp_message`) -/// that are used to verify the authenticity and integrity of a request to the proxy. This is essential for securing -/// the proxy operations and preventing unauthorized access. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub(crate) struct SignedMessage { - pub(crate) coin_ticker: String, - pub(crate) address: String, - pub(crate) timestamp_message: u64, - pub(crate) signature: String, -} - -impl SignOps for SignedMessage { - // TODO: include coin ticker in the message signing algorithm - fn sign_message_hash(&self) -> [u8; 32] { - let prefix = "atomicDEX Auth Ethereum Signed Message:\n"; - let mut stream = Stream::new(); - let prefix_len = CompactInteger::from(prefix.len()); - prefix_len.serialize(&mut stream); - stream.append_slice(prefix.as_bytes()); - stream.append_slice( - self.timestamp_message - .to_string() - .len() - .to_string() - .as_bytes(), - ); - stream.append_slice(self.timestamp_message.to_string().as_bytes()); - keccak256(&stream.out()).take() - } - - /// Displays the address in mixed-case checksum form - /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md - fn checksum_address(&self) -> String { - let mut addr = self.address.to_lowercase(); - if addr.starts_with("0x") { - addr.replace_range(..2, ""); - } - - let mut hasher = Keccak256::default(); - hasher.update(&addr); - let hash = hasher.finalize(); - let mut result: String = "0x".into(); - for (i, c) in addr.chars().enumerate() { - #[allow(clippy::is_digit_ascii_radix)] - if c.is_digit(10) { - result.push(c); - } else { - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md#specification - // Convert the address to hex, but if the ith digit is a letter (ie. it's one of abcdef) - // print it in uppercase if the 4*ith bit of the hash of the lowercase hexadecimal - // address is 1 otherwise print it in lowercase. - if hash[i / 2] & (1 << (7 - 4 * (i % 2))) != 0 { - result.push(c.to_ascii_uppercase()); - } else { - result.push(c.to_ascii_lowercase()); - } - } - } - - result - } - - fn is_valid_checksum_addr(&self) -> bool { - self.address == self.checksum_address() - } - - fn valid_addr_from_str(&self) -> Result { - let addr = self.addr_from_str()?; - if !self.is_valid_checksum_addr() { - return Err(String::from("Invalid address checksum")); - } - Ok(addr) - } - - fn addr_from_str(&self) -> Result { - if !self.address.starts_with("0x") { - return Err(String::from("Address must be prefixed with 0x")); - }; - - Address::from_str(&self.address[2..]).map_err(|e| e.to_string()) - } - - #[allow(dead_code)] - fn sign_message(&mut self, secret: &Secret) -> GenericResult<()> { - let signature = sign(secret, &H256::from(self.sign_message_hash()))?; - self.signature = format!("0x{}", signature); - - Ok(()) - } - - fn verify_message(&self) -> GenericResult { - let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); - - // TODO: we should also validate if the timestamp is too far from now. - if now > self.timestamp_message { - return Ok(false); - } - - let message_hash = self.sign_message_hash(); - let address = self.valid_addr_from_str()?; - - let signature = - Signature::from_str(self.signature.strip_prefix("0x").unwrap_or(&self.signature))?; - - Ok(verify_address( - &address, - &signature, - &H256::from(message_hash), - )?) - } -} - -#[test] -fn test_signed_message_serialzation_and_deserialization() { - let json_signed_message = serde_json::json!({ - "address": "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", - "timestamp_message": 0, - "signature": "", - "coin_ticker": "ETH" - }); - - let actual_signed_message: SignedMessage = - serde_json::from_str(&json_signed_message.to_string()).unwrap(); - - let expected_signed_message = SignedMessage { - address: String::from("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"), - timestamp_message: u64::default(), - signature: String::new(), - coin_ticker: String::from("ETH"), - }; - - assert_eq!(actual_signed_message, expected_signed_message); - - // Backwards - let json = serde_json::to_value(expected_signed_message).unwrap(); - assert_eq!(json_signed_message, json); - assert_eq!(json_signed_message.to_string(), json.to_string()); -} - -#[test] -fn test_sign_message_hash() { - let mut signed_message = SignedMessage { - address: String::from("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"), - timestamp_message: u64::default(), - signature: String::default(), - coin_ticker: String::from("ETH"), - }; - - let msg = signed_message.sign_message_hash(); - assert_eq!( - msg, - [ - 58, 69, 48, 53, 234, 107, 112, 243, 143, 137, 89, 208, 73, 115, 136, 31, 254, 255, 243, - 123, 197, 144, 241, 223, 80, 91, 195, 194, 192, 86, 180, 33 - ] - ); - - signed_message.timestamp_message = 1655376657; - let msg = signed_message.sign_message_hash(); - assert_eq!( - msg, - [ - 178, 222, 11, 225, 166, 231, 156, 50, 173, 22, 122, 90, 196, 182, 121, 168, 218, 27, 4, - 223, 95, 245, 64, 131, 181, 196, 108, 220, 13, 219, 36, 94 - ] - ); - - signed_message.coin_ticker = String::default(); - let msg = signed_message.sign_message_hash(); - assert_eq!( - msg, - [ - 178, 222, 11, 225, 166, 231, 156, 50, 173, 22, 122, 90, 196, 182, 121, 168, 218, 27, 4, - 223, 95, 245, 64, 131, 181, 196, 108, 220, 13, 219, 36, 94 - ] - ); -} - -#[test] -fn test_checksum_addr() { - let mut signed_message = SignedMessage { - address: String::from("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"), - timestamp_message: u64::default(), - signature: String::new(), - coin_ticker: String::from("ETH"), - }; - - assert_eq!( - signed_message.checksum_address(), - "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359" - ); - - signed_message.address = String::from("0x52908400098527886E0F7030069857D2E4169EE7"); - assert_eq!( - signed_message.checksum_address(), - "0x52908400098527886E0F7030069857D2E4169EE7" - ); - - signed_message.address = String::from("0x8617e340b3d01fa5f11f306f4090fd50e238070d"); - assert_eq!( - signed_message.checksum_address(), - "0x8617E340B3D01FA5F11F306F4090FD50E238070D" - ); - - signed_message.address = String::from("0xde709f2102306220921060314715629080e2fb77"); - assert_eq!( - signed_message.checksum_address(), - "0xde709f2102306220921060314715629080e2fb77" - ); - - signed_message.address = String::from("0x27b1fdb04752bbc536007a920d24acb045561c26"); - assert_eq!( - signed_message.checksum_address(), - "0x27b1fdb04752bbc536007a920d24acb045561c26" - ); - - signed_message.address = String::from("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); - assert_eq!( - signed_message.checksum_address(), - "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" - ); -} - -#[test] -fn test_is_valid_checksum_addr() { - let mut signed_message = SignedMessage { - address: String::from("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"), - timestamp_message: u64::default(), - signature: String::new(), - coin_ticker: String::from("ETH"), - }; - assert!(signed_message.is_valid_checksum_addr()); - - signed_message.address = String::from("0x52908400098527886E0F7030069857D2E4169EE7"); - assert!(signed_message.is_valid_checksum_addr()); - - signed_message.address = String::from("0x8617e340B3D01FA5F11F306F4090FD50E238070D"); - assert!(!signed_message.is_valid_checksum_addr()); - - signed_message.address = String::from("0xd1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); - assert!(!signed_message.is_valid_checksum_addr()); -} - -#[test] -fn test_addr_from_str_and_valid_addr_from_str() { - let mut signed_message = SignedMessage { - address: String::from("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"), - timestamp_message: u64::default(), - signature: String::new(), - coin_ticker: String::from("ETH"), - }; - signed_message.addr_from_str().unwrap(); - signed_message.valid_addr_from_str().unwrap(); - - signed_message.address = String::from("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); - signed_message.addr_from_str().unwrap(); - signed_message.valid_addr_from_str().unwrap(); - - signed_message.address = String::from("0x52908400098527886E0F7030069857D2E4169EE7"); - signed_message.addr_from_str().unwrap(); - signed_message.valid_addr_from_str().unwrap(); - - signed_message.address = String::from("0x709f2102306220921060314715629080e2fb77"); - signed_message.addr_from_str().unwrap_err(); - signed_message.valid_addr_from_str().unwrap_err(); - - signed_message.address = String::from("0x27b1fdb04752bbc536007a920d2"); - signed_message.addr_from_str().unwrap_err(); - signed_message.valid_addr_from_str().unwrap_err(); - - signed_message.address = String::from("5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); - signed_message.addr_from_str().unwrap_err(); - signed_message.valid_addr_from_str().unwrap_err(); -} - -#[test] -fn test_message_sign_and_verify() { - let timestamp_message = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - - let mut signed_message = SignedMessage { - address: String::from("0xbAB36286672fbdc7B250804bf6D14Be0dF69fa29"), - timestamp_message: timestamp_message - 5 * 60, - signature: String::new(), - coin_ticker: String::from("ETH"), - }; - - let key_pair = ethkey::KeyPair::from_secret_slice( - &hex::decode("809465b17d0a4ddb3e4c69e8f23c2cabad868f51f8bed5c765ad1d6516c3306f").unwrap(), - ) - .unwrap(); - - signed_message.sign_message(key_pair.secret()).unwrap(); - assert!(!signed_message.verify_message().unwrap()); - - signed_message.timestamp_message = timestamp_message + 5 * 60; - signed_message.sign_message(key_pair.secret()).unwrap(); - assert!(signed_message.verify_message().unwrap()); -} From 1aa6e5ca765c835302c9e606b0a9c05ec64c81c1 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 30 Jul 2024 08:45:31 +0300 Subject: [PATCH 05/23] use peer address Signed-off-by: onur-ozkan --- src/proxy/quicknode.rs | 126 ++++++----------------------------------- 1 file changed, 17 insertions(+), 109 deletions(-) diff --git a/src/proxy/quicknode.rs b/src/proxy/quicknode.rs index a886515..a59d603 100644 --- a/src/proxy/quicknode.rs +++ b/src/proxy/quicknode.rs @@ -74,7 +74,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, req.uri(), "Method {} not allowed for, returning 403.", payload.method @@ -93,7 +93,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, req.uri(), "Error inserting JWT into http header, returning 500." ) @@ -110,7 +110,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, original_req_uri, "Error type casting value of {} into Uri: {}, returning 500.", proxy_route.outbound_route, @@ -139,7 +139,7 @@ pub(crate) async fn proxy_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, original_req_uri, "Couldn't reach {}: {}. Returning 503.", target_uri, @@ -162,7 +162,7 @@ pub(crate) async fn validation_middleware_quicknode( ) -> Result<(), StatusCode> { let mut db = Db::create_instance(cfg).await; - match db.read_address_status("ADDRESS_TODO").await { + match db.read_address_status(&signed_message.address).await { AddressStatus::Trusted => Ok(()), AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), AddressStatus::None => { @@ -174,7 +174,7 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, req_uri, "Request has invalid signed message, returning 401" ) @@ -183,8 +183,7 @@ pub(crate) async fn validation_middleware_quicknode( return Err(StatusCode::UNAUTHORIZED); }; - let rate_limiter_key = - format!("{}:{}", "TICKER_TODO", "ADDRESS_TODO"); + let rate_limiter_key = format!("{}:{}", "TICKER_TODO", signed_message.address); let rate_limiter = proxy_route .rate_limiter @@ -197,11 +196,11 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, req_uri, "Rate exceed for {}, checking balance for {} address.", rate_limiter_key, - "ADDRESS_TODO" + signed_message.address, ) ); @@ -212,10 +211,10 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, req_uri, "Wallet {} has insufficient balance for coin {}, returning 406.", - "ADDRESS_TODO", + signed_message.address, "TICKER_TODO", ) ); @@ -227,7 +226,7 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, req_uri, "verify_message_and_balance failed in coin {}: {:?}", "TICKER_TODO", @@ -245,7 +244,7 @@ pub(crate) async fn validation_middleware_quicknode( "{}", log_format!( remote_addr.ip(), - "ADDRESS_TODO", + signed_message.address, req_uri, "Rate incrementing failed." ) @@ -257,6 +256,7 @@ pub(crate) async fn validation_middleware_quicknode( } } +// TODO: don't check balance async fn verify_message_and_balance( cfg: &AppConfig, signed_message: &ProxySign, @@ -266,7 +266,7 @@ async fn verify_message_and_balance( let mut db = Db::create_instance(cfg).await; // We don't want to send balance requests everytime when user sends requests. - if let Ok(true) = db.key_exists("ADDRESS_TODO").await { + if let Ok(true) = db.key_exists(&signed_message.address).await { return Ok(()); } @@ -274,7 +274,7 @@ async fn verify_message_and_balance( "id": 1, "jsonrpc": "2.0", "method": "eth_getBalance", - "params": ["ADDRESS_TODO", "latest"] + "params": [signed_message.address, "latest"] }); let rpc_client = @@ -287,7 +287,7 @@ async fn verify_message_and_balance( { Ok(res) if res["result"] != Json::Null && res["result"] != "0x0" => { // cache this address for 60 seconds - let _ = db.insert_cache("ADDRESS_TODO", "", 60).await; + let _ = db.insert_cache(&signed_message.address, "", 60).await; return Ok(()); } @@ -301,95 +301,3 @@ async fn verify_message_and_balance( Err(ProofOfFundingError::InvalidSignedMessage) } - -#[test] -fn test_quicknode_payload_serialzation_and_deserialization() { - let json_payload = json!({ - "method": "dummy-value", - "params": [], - "id": 1, - "jsonrpc": "2.0" - }); - - let actual_payload: QuicknodePayload = serde_json::from_str(&json_payload.to_string()).unwrap(); - - let expected_payload = QuicknodePayload { - method: String::from("dummy-value"), - params: json!([]), - id: 1, - jsonrpc: String::from("2.0"), - }; - - assert_eq!(actual_payload, expected_payload); - - // Backwards - let json = serde_json::to_value(expected_payload).unwrap(); - assert_eq!(json_payload, json); - assert_eq!(json_payload.to_string(), json.to_string()); -} - -#[tokio::test] -async fn test_parse_quicknode_payload() { - use super::{parse_body_and_auth_header, X_AUTH_PAYLOAD}; - use hyper::Method; - - let serialized_payload = json!({ - "method": "dummy-value", - "params": [], - "id": 1, - "jsonrpc": "2.0" - }) - .to_string(); - - let serialized_auth_value = json!({ - "coin_ticker": "ETH", - "address": "dummy-value", - "timestamp_message": 1655319963, - "signature": "dummy-value", - }) - .to_string(); - - let mut req = Request::builder() - .method(Method::POST) - .header( - X_AUTH_PAYLOAD, - HeaderValue::from_str(&serialized_auth_value).unwrap(), - ) - .body(Body::from(serialized_payload)) - .unwrap(); - req.headers_mut().insert( - HeaderName::from_static("dummy-header"), - "dummy-value".parse().unwrap(), - ); - - let (mut req, payload, signed_message) = parse_body_and_auth_header::(req) - .await - .unwrap(); - - let body_bytes = hyper::body::to_bytes(req.body_mut()).await.unwrap(); - assert!( - !body_bytes.is_empty(), - "Body should not be empty for non-GET methods" - ); - - let dummy_header_value = req.headers().get("dummy-header").unwrap(); - assert_eq!(dummy_header_value, "dummy-value"); - - let expected_payload = QuicknodePayload { - method: String::from("dummy-value"), - params: json!([]), - id: 1, - jsonrpc: String::from("2.0"), - }; - assert_eq!(payload, expected_payload); - - let expected_auth_value = SignedMessage { - coin_ticker: String::from("ETH"), - address: String::from("dummy-value"), - timestamp_message: 1655319963, - signature: String::from("dummy-value"), - }; - assert_eq!(signed_message, expected_auth_value); - - remove_hop_by_hop_headers(&mut req, &[]).unwrap(); -} From 81f9577f914066a5a8bf1d9a3297a6b5c1fb2cee Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 30 Jul 2024 12:19:54 +0300 Subject: [PATCH 06/23] WIP: replace `SignedMessage` with upstream `ProxySign` Signed-off-by: onur-ozkan --- src/net/server.rs | 1 + src/proxy/mod.rs | 22 ++++----- src/proxy/moralis.rs | 85 ++++++++++++++-------------------- src/proxy/quicknode.rs | 103 +++++++---------------------------------- 4 files changed, 64 insertions(+), 147 deletions(-) diff --git a/src/net/server.rs b/src/net/server.rs index 790a3ff..c93b378 100644 --- a/src/net/server.rs +++ b/src/net/server.rs @@ -11,6 +11,7 @@ use crate::http::{http_handler, response_by_status, X_FORWARDED_FOR}; use crate::log_format; use crate::websocket::{should_upgrade_to_socket_conn, socket_handler}; +// TODO: replace this macro with a helper function. #[macro_export] macro_rules! log_format { ($ip: expr, $address: expr, $path: expr, $format: expr, $($args: tt)+) => {format!(concat!("[Ip: {} | Address: {} | Path: {}] ", $format), $ip, $address, $path, $($args)+)}; diff --git a/src/proxy/mod.rs b/src/proxy/mod.rs index 8471b13..87ce71d 100644 --- a/src/proxy/mod.rs +++ b/src/proxy/mod.rs @@ -1,8 +1,8 @@ use crate::ctx::{AppConfig, GenericResult, ProxyRoute}; -use crate::sign::SignedMessage; use hyper::header; use hyper::header::{HeaderName, HeaderValue}; use hyper::{Body, Request, Response, StatusCode, Uri}; +use proxy_signature::ProxySign; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::net::SocketAddr; @@ -33,18 +33,18 @@ pub(crate) enum PayloadData { /// Quicknode feature requires body payload and Signed Message in X-Auth-Payload header Quicknode { payload: QuicknodePayload, - signed_message: SignedMessage, + signed_message: ProxySign, }, /// Moralis feature requires only Signed Message in X-Auth-Payload header and doesn't have body - Moralis(SignedMessage), + Moralis(ProxySign), } impl PayloadData { - /// Returns a reference to the `SignedMessage` contained within the payload. - pub(crate) fn signed_message(&self) -> &SignedMessage { + /// Returns a reference to the `ProxySign` contained within the payload. + pub(crate) fn signed_message(&self) -> &ProxySign { match self { PayloadData::Quicknode { signed_message, .. } => signed_message, - PayloadData::Moralis(payload) => payload, + PayloadData::Moralis(signed_message) => signed_message, } } } @@ -137,7 +137,7 @@ pub(crate) async fn validation_middleware( /// If the body is empty or the header is missing, an error is returned. async fn parse_body_and_auth_header( req: Request, -) -> GenericResult<(Request, T, SignedMessage)> +) -> GenericResult<(Request, T, ProxySign)> where T: DeserializeOwned, { @@ -147,7 +147,7 @@ where .get(X_AUTH_PAYLOAD) .ok_or("Missing X-Auth-Payload header")? .to_str()?; - let signed_message: SignedMessage = serde_json::from_str(header_value)?; + let signed_message: ProxySign = serde_json::from_str(header_value)?; let body_bytes = hyper::body::to_bytes(body).await?; if body_bytes.is_empty() { return Err("Empty body cannot be deserialized into non-optional type T".into()); @@ -157,15 +157,15 @@ where Ok((new_req, payload, signed_message)) } -/// Parses [SignedMessage] value from X-Auth-Payload header -async fn parse_auth_header(req: Request) -> GenericResult<(Request, SignedMessage)> { +/// Parses [ProxySign] value from X-Auth-Payload header +async fn parse_auth_header(req: Request) -> GenericResult<(Request, ProxySign)> { let (parts, body) = req.into_parts(); let header_value = parts .headers .get(X_AUTH_PAYLOAD) .ok_or("Missing X-Auth-Payload header")? .to_str()?; - let payload: SignedMessage = serde_json::from_str(header_value)?; + let payload: ProxySign = serde_json::from_str(header_value)?; let new_req = Request::from_parts(parts, body); Ok((new_req, payload)) } diff --git a/src/proxy/moralis.rs b/src/proxy/moralis.rs index a997f05..74a6508 100644 --- a/src/proxy/moralis.rs +++ b/src/proxy/moralis.rs @@ -6,18 +6,18 @@ use crate::http::{ }; use crate::proxy::remove_hop_by_hop_headers; use crate::rate_limiter::RateLimitOperations; -use crate::sign::{SignOps, SignedMessage}; use crate::{log_format, GenericResult}; use hyper::header::{HeaderName, HeaderValue}; use hyper::{header, Body, Request, Response, StatusCode, Uri}; use hyper_tls::HttpsConnector; +use proxy_signature::ProxySign; use std::net::SocketAddr; pub(crate) async fn proxy_moralis( cfg: &AppConfig, mut req: Request, remote_addr: &SocketAddr, - signed_message: SignedMessage, + signed_message: ProxySign, x_forwarded_for: HeaderValue, proxy_route: &ProxyRoute, ) -> GenericResult> { @@ -113,7 +113,7 @@ fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> Gene pub(crate) async fn validation_middleware_moralis( cfg: &AppConfig, - signed_message: &SignedMessage, + signed_message: &ProxySign, proxy_route: &ProxyRoute, req_uri: &Uri, remote_addr: &SocketAddr, @@ -124,39 +124,22 @@ pub(crate) async fn validation_middleware_moralis( AddressStatus::Trusted => Ok(()), AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), AddressStatus::None => { - match signed_message.verify_message() { - Ok(true) => {} - Ok(false) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Request has invalid signed message, returning 401" - ) - ); + if !signed_message.is_valid_message() { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req_uri, + "Request has invalid signed message, returning 401" + ) + ); - return Err(StatusCode::UNAUTHORIZED); - } - Err(e) => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "verify_message failed in coin {}: {}, returning 500.", - signed_message.coin_ticker, - e - ) - ); - return Err(StatusCode::INTERNAL_SERVER_ERROR); - } + return Err(StatusCode::UNAUTHORIZED); } let rate_limiter_key = - format!("{}:{}", signed_message.coin_ticker, signed_message.address); + format!("{}:{}", proxy_route.inbound_route, signed_message.address); let rate_limiter = proxy_route .rate_limiter @@ -184,8 +167,8 @@ pub(crate) async fn validation_middleware_moralis( remote_addr.ip(), signed_message.address, req_uri, - "Rate exceeded check failed in coin {}: {}, returning 500.", - signed_message.coin_ticker, + "Rate exceeded check failed for node '{}': {}, returning 500.", + signed_message.address, e ) ); @@ -200,8 +183,8 @@ pub(crate) async fn validation_middleware_moralis( remote_addr.ip(), signed_message.address, req_uri, - "Rate incrementing failed in coin {}: {}, returning 500.", - signed_message.coin_ticker, + "Rate incrementing failed for node '{}': {}, returning 500.", + signed_message.address, e ) ); @@ -247,21 +230,21 @@ async fn test_parse_moralis_payload() { let header_value = req.headers().get(header::ACCEPT).unwrap(); - let expected_payload = SignedMessage { - coin_ticker: String::from("BTC"), - address: String::from("dummy-value"), - timestamp_message: 1655320000, - signature: String::from("dummy-value"), - }; - - assert_eq!(payload, expected_payload); - assert_eq!(header_value, APPLICATION_JSON); - - let additional_headers = &[ - header::CONTENT_LENGTH, - HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), - ]; - remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); + // let expected_payload = SignedMessage { + // coin_ticker: String::from("BTC"), + // address: String::from("dummy-value"), + // timestamp_message: 1655320000, + // signature: String::from("dummy-value"), + // }; + + // assert_eq!(payload, expected_payload); + // assert_eq!(header_value, APPLICATION_JSON); + + // let additional_headers = &[ + // header::CONTENT_LENGTH, + // HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), + // ]; + // remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); } #[tokio::test] diff --git a/src/proxy/quicknode.rs b/src/proxy/quicknode.rs index a59d603..947b8ab 100644 --- a/src/proxy/quicknode.rs +++ b/src/proxy/quicknode.rs @@ -6,14 +6,12 @@ use crate::http::{ }; use crate::proxy::remove_hop_by_hop_headers; use crate::rate_limiter::RateLimitOperations; -use crate::rpc::Json; -use crate::{log_format, rpc, GenericResult}; +use crate::{log_format, GenericResult}; use hyper::header::{HeaderName, HeaderValue}; use hyper::{header, Body, Request, Response, StatusCode, Uri}; use hyper_tls::HttpsConnector; use proxy_signature::ProxySign; use serde::{Deserialize, Serialize}; -use serde_json::json; use std::net::SocketAddr; /// Represents a payload for JSON-RPC calls, tailored for the Quicknode API within the proxy. @@ -166,10 +164,7 @@ pub(crate) async fn validation_middleware_quicknode( AddressStatus::Trusted => Ok(()), AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), AddressStatus::None => { - let signed_message_status = - verify_message_and_balance(cfg, signed_message, proxy_route).await; - - if let Err(ProofOfFundingError::InvalidSignedMessage) = signed_message_status { + if !signed_message.is_valid_message() { log::warn!( "{}", log_format!( @@ -183,7 +178,8 @@ pub(crate) async fn validation_middleware_quicknode( return Err(StatusCode::UNAUTHORIZED); }; - let rate_limiter_key = format!("{}:{}", "TICKER_TODO", signed_message.address); + let rate_limiter_key = + format!("{}:{}", proxy_route.inbound_route, signed_message.address); let rate_limiter = proxy_route .rate_limiter @@ -204,37 +200,20 @@ pub(crate) async fn validation_middleware_quicknode( ) ); - match verify_message_and_balance(cfg, signed_message, proxy_route).await { - Ok(_) => {} - Err(ProofOfFundingError::InsufficientBalance) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Wallet {} has insufficient balance for coin {}, returning 406.", - signed_message.address, - "TICKER_TODO", - ) - ); - - return Err(StatusCode::NOT_ACCEPTABLE); - } - e => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "verify_message_and_balance failed in coin {}: {:?}", - "TICKER_TODO", - e - ) - ); - return Err(StatusCode::INTERNAL_SERVER_ERROR); - } + if !signed_message.is_valid_message() { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req_uri, + "Node '{}' sent invalid signed message to inbound '{}', returning 401.", + signed_message.address, + proxy_route.inbound_route + ) + ); + + return Err(StatusCode::UNAUTHORIZED); } } }; @@ -255,49 +234,3 @@ pub(crate) async fn validation_middleware_quicknode( } } } - -// TODO: don't check balance -async fn verify_message_and_balance( - cfg: &AppConfig, - signed_message: &ProxySign, - proxy_route: &ProxyRoute, -) -> Result<(), ProofOfFundingError> { - if let true = signed_message.is_valid_message() { - let mut db = Db::create_instance(cfg).await; - - // We don't want to send balance requests everytime when user sends requests. - if let Ok(true) = db.key_exists(&signed_message.address).await { - return Ok(()); - } - - let rpc_payload = json!({ - "id": 1, - "jsonrpc": "2.0", - "method": "eth_getBalance", - "params": [signed_message.address, "latest"] - }); - - let rpc_client = - // TODO: Use the current transport instead of forcing to use http (even if it's rare, this might not work on certain nodes) - rpc::RpcClient::new(proxy_route.outbound_route.replace("ws", "http").clone()); - - match rpc_client - .send(cfg, rpc_payload, proxy_route.authorized) - .await - { - Ok(res) if res["result"] != Json::Null && res["result"] != "0x0" => { - // cache this address for 60 seconds - let _ = db.insert_cache(&signed_message.address, "", 60).await; - - return Ok(()); - } - Ok(res) if res["error"] != Json::Null => { - return Err(ProofOfFundingError::ErrorFromRpcCall); - } - Ok(_) => return Err(ProofOfFundingError::InsufficientBalance), - Err(e) => return Err(ProofOfFundingError::RpcCallFailed(e.to_string())), - }; - } - - Err(ProofOfFundingError::InvalidSignedMessage) -} From b74827f119a514806f97b627efe28237449395da Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 30 Jul 2024 12:21:22 +0300 Subject: [PATCH 07/23] embed some TODO notes Signed-off-by: onur-ozkan --- .TODO | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .TODO diff --git a/.TODO b/.TODO new file mode 100644 index 0000000..686d42e --- /dev/null +++ b/.TODO @@ -0,0 +1,3 @@ +- Simpler logging mechanism +- Test coverage +- Peer verification through KDF RPCs From d6e51c7a4efb532ae61fc2fbaec026a1ca2b2d74 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Wed, 31 Jul 2024 16:12:37 +0300 Subject: [PATCH 08/23] unifiy proxy logic and refactor the basics Signed-off-by: onur-ozkan --- Cargo.toml | 2 +- src/db.rs | 2 + src/main.rs | 2 - src/net/http.rs | 4 +- src/net/rpc.rs | 45 +++ src/net/server.rs | 2 +- src/proxy/http/mod.rs | 503 ++++++++++++++++++++++++++++++++ src/proxy/mod.rs | 93 ++++-- src/proxy/moralis.rs | 304 ------------------- src/proxy/quicknode.rs | 236 --------------- src/{net => proxy}/websocket.rs | 11 +- 11 files changed, 622 insertions(+), 582 deletions(-) create mode 100644 src/proxy/http/mod.rs delete mode 100644 src/proxy/moralis.rs delete mode 100644 src/proxy/quicknode.rs rename src/{net => proxy}/websocket.rs (96%) diff --git a/Cargo.toml b/Cargo.toml index a6e2776..1421eaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ once_cell = "1.12.0" url = { version = "2.2.2", features = ["serde"] } redis = { version = "0.21.5", default-features = false, features = ["tokio-comp"] } serde = "1.0.137" -serde_json = "1.0.81" +serde_json = { version = "1.0.81", features = ["preserve_order", "raw_value"] } sha3 = "0.9" simple_logger = "2.1.0" tokio = { version = "1.12.0", default-features = false, features = ["macros", "rt-multi-thread", "sync", "time"] } diff --git a/src/db.rs b/src/db.rs index 1146b6e..e734683 100644 --- a/src/db.rs +++ b/src/db.rs @@ -36,6 +36,7 @@ impl Db { } } + #[allow(dead_code)] pub(crate) async fn key_exists(&mut self, key: &str) -> GenericResult { Ok(redis::cmd("EXISTS") .arg(key) @@ -43,6 +44,7 @@ impl Db { .await?) } + #[allow(dead_code)] pub(crate) async fn insert_cache( &mut self, key: &str, diff --git a/src/main.rs b/src/main.rs index f4f041c..372dde5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,6 @@ mod rate_limiter; mod rpc; #[path = "net/server.rs"] mod server; -#[path = "net/websocket.rs"] -mod websocket; #[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "gnu"))] #[global_allocator] diff --git a/src/net/http.rs b/src/net/http.rs index 41a5c31..d427a5d 100644 --- a/src/net/http.rs +++ b/src/net/http.rs @@ -147,7 +147,7 @@ pub(crate) async fn http_handler( "{}", log_format!( remote_addr.ip(), - payload.signed_message().address, + payload.proxy_sign().address, req_uri, "Request and payload data received." ) @@ -160,7 +160,7 @@ pub(crate) async fn http_handler( "{}", log_format!( remote_addr.ip(), - payload.signed_message().address, + payload.proxy_sign().address, req_uri, "Error type casting of IpAddr into HeaderValue, returning 500." ) diff --git a/src/net/rpc.rs b/src/net/rpc.rs index 3256311..f570c61 100644 --- a/src/net/rpc.rs +++ b/src/net/rpc.rs @@ -1,8 +1,12 @@ +#![allow(dead_code)] // We will need this module for KDF RPCs + use bytes::Buf; use ctx::AppConfig; use http::{insert_jwt_to_http_header, APPLICATION_JSON}; use hyper::{body::aggregate, header, Body, Request}; use hyper_tls::HttpsConnector; +use proxy_signature::ProxySign; +use serde::{Deserialize, Serialize}; use serde_json::from_reader; use super::*; @@ -14,6 +18,47 @@ pub(crate) struct RpcClient { pub(crate) url: String, } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[serde(untagged)] +pub(crate) enum Id { + String(String), + Number(usize), +} + +/// Payload for JSON-RPC calls +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub(crate) struct RpcPayload { + pub(crate) method: String, + pub(crate) params: serde_json::value::Value, + pub(crate) id: Id, + pub(crate) jsonrpc: String, +} + +/// Used for websocket connection. +/// It combines standard JSON RPC method call fields (method, params, id, jsonrpc) with a `SignedMessage` +/// for authentication and validation, facilitating secure and validated interactions with the Quicknode service. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub(crate) struct RpcSocketPayload { + pub(crate) method: String, + pub(crate) params: serde_json::value::Value, + pub(crate) id: Id, + pub(crate) jsonrpc: String, + pub(crate) proxy_sign: ProxySign, +} + +impl RpcSocketPayload { + pub(crate) fn into_parts(self) -> (RpcPayload, ProxySign) { + let payload = RpcPayload { + method: self.method, + params: self.params, + id: self.id, + jsonrpc: self.jsonrpc, + }; + let proxy_sign = self.proxy_sign; + (payload, proxy_sign) + } +} + impl RpcClient { pub(crate) fn new(url: String) -> Self { RpcClient { url } diff --git a/src/net/server.rs b/src/net/server.rs index c93b378..aa4603c 100644 --- a/src/net/server.rs +++ b/src/net/server.rs @@ -9,7 +9,7 @@ use super::{GenericError, GenericResult}; use crate::ctx::{AppConfig, DEFAULT_PORT}; use crate::http::{http_handler, response_by_status, X_FORWARDED_FOR}; use crate::log_format; -use crate::websocket::{should_upgrade_to_socket_conn, socket_handler}; +use crate::proxy::websocket::{should_upgrade_to_socket_conn, socket_handler}; // TODO: replace this macro with a helper function. #[macro_export] diff --git a/src/proxy/http/mod.rs b/src/proxy/http/mod.rs new file mode 100644 index 0000000..0ef1557 --- /dev/null +++ b/src/proxy/http/mod.rs @@ -0,0 +1,503 @@ +pub(crate) mod get { + use crate::address_status::{AddressStatus, AddressStatusOperations}; + use crate::ctx::{AppConfig, ProxyRoute}; + use crate::db::Db; + use crate::http::{ + insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, + }; + use crate::proxy::remove_hop_by_hop_headers; + use crate::rate_limiter::RateLimitOperations; + use crate::{log_format, GenericResult}; + use hyper::header::{HeaderName, HeaderValue}; + use hyper::{header, Body, Request, Response, StatusCode, Uri}; + use hyper_tls::HttpsConnector; + use proxy_signature::ProxySign; + use std::net::SocketAddr; + + pub(crate) async fn proxy( + cfg: &AppConfig, + mut req: Request, + remote_addr: &SocketAddr, + signed_message: ProxySign, + x_forwarded_for: HeaderValue, + proxy_route: &ProxyRoute, + ) -> GenericResult> { + if proxy_route.authorized { + if let Err(e) = insert_jwt_to_http_header(cfg, req.headers_mut()).await { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req.uri(), + "Error inserting JWT into HTTP header: {}, returning 500.", + e + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let original_req_uri = req.uri().clone(); + + if let Err(e) = modify_request_uri(&mut req, proxy_route) { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + original_req_uri, + "Error modifying request base Uri: {}, returning 500.", + e + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + + remove_hop_by_hop_headers(&mut req, &[header::CONTENT_LENGTH])?; + + req.headers_mut() + .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); + req.headers_mut() + .insert(header::ACCEPT, APPLICATION_JSON.parse()?); + + let https = HttpsConnector::new(); + let client = hyper::Client::builder().build(https); + + let target_uri = req.uri().clone(); + let res = match client.request(req).await { + Ok(t) => t, + Err(e) => { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + original_req_uri, + "Couldn't reach {}: {}. Returning 503.", + target_uri, + e + ) + ); + return response_by_status(StatusCode::SERVICE_UNAVAILABLE); + } + }; + + Ok(res) + } + + /// This function removes the matched inbound route from the request URI and + /// replaces request base URI with the outbound route specified in the proxy route. + fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> GenericResult<()> { + let proxy_base_uri = proxy_route.outbound_route.parse::()?; + let original_uri = req.uri(); + + let original_path_and_query = original_uri + .path_and_query() + .map(|pq| pq.as_str()) + .unwrap_or(""); + // Remove the "inbound_route" part from the original path and query + let remaining_path_and_query = if proxy_route.inbound_route == "/" { + original_path_and_query + } else { + original_path_and_query + .strip_prefix(&proxy_route.inbound_route) + .ok_or("Route doesn't match with the given inbound URL.")? + }; + + let mut base_uri_parts = proxy_base_uri.into_parts(); + base_uri_parts.path_and_query = Some(remaining_path_and_query.parse()?); + let new_uri = Uri::from_parts(base_uri_parts)?; + *req.uri_mut() = new_uri; + Ok(()) + } + + pub(crate) async fn validation_middleware( + cfg: &AppConfig, + signed_message: &ProxySign, + proxy_route: &ProxyRoute, + req_uri: &Uri, + remote_addr: &SocketAddr, + ) -> Result<(), StatusCode> { + let mut db = Db::create_instance(cfg).await; + + match db.read_address_status(&signed_message.address).await { + AddressStatus::Trusted => Ok(()), + AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), + AddressStatus::None => { + if !signed_message.is_valid_message() { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req_uri, + "Request has invalid signed message, returning 401" + ) + ); + + return Err(StatusCode::UNAUTHORIZED); + } + + let rate_limiter_key = + format!("{}:{}", proxy_route.inbound_route, signed_message.address); + + let rate_limiter = proxy_route + .rate_limiter + .as_ref() + .unwrap_or(&cfg.rate_limiter); + match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { + Ok(false) => {} + Ok(true) => { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req_uri, + "Rate exceed for {}, returning 406.", + rate_limiter_key, + ) + ); + return Err(StatusCode::NOT_ACCEPTABLE); + } + Err(e) => { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req_uri, + "Rate exceeded check failed for node '{}': {}, returning 500.", + signed_message.address, + e + ) + ); + return Err(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + if let Err(e) = db.rate_address(rate_limiter_key).await { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req_uri, + "Rate incrementing failed for node '{}': {}, returning 500.", + signed_message.address, + e + ) + ); + return Err(StatusCode::INTERNAL_SERVER_ERROR); + }; + + Ok(()) + } + } + } + + #[tokio::test] + async fn test_parse_payload() { + // use hyper::header::HeaderName; + use hyper::Method; + + let serialized_payload = serde_json::json!({ + "coin_ticker": "BTC", + "address": "dummy-value", + "timestamp_message": 1655320000, + "signature": "dummy-value", + }) + .to_string(); + + let req = Request::builder() + .method(Method::GET) + .header(header::ACCEPT, HeaderValue::from_static(APPLICATION_JSON)) + .header( + crate::proxy::X_AUTH_PAYLOAD, + HeaderValue::from_str(&serialized_payload).unwrap(), + ) + .body(Body::empty()) + .unwrap(); + + let (mut req, payload) = crate::proxy::parse_auth_header(req).await.unwrap(); + + let body_bytes = hyper::body::to_bytes(req.body_mut()).await.unwrap(); + assert!( + body_bytes.is_empty(), + "Body should be empty for GET methods" + ); + + let header_value = req.headers().get(header::ACCEPT).unwrap(); + + // let expected_payload = SignedMessage { + // coin_ticker: String::from("BTC"), + // address: String::from("dummy-value"), + // timestamp_message: 1655320000, + // signature: String::from("dummy-value"), + // }; + + // assert_eq!(payload, expected_payload); + // assert_eq!(header_value, APPLICATION_JSON); + + // let additional_headers = &[ + // header::CONTENT_LENGTH, + // HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), + // ]; + // remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); + } + + #[tokio::test] + async fn test_modify_request_uri() { + use std::str::FromStr; + use crate::proxy::ProxyType; + + const EXPECTED_URI: &str = "http://localhost:8000/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC"; + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/nft-test/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/nft-test").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!( + req.uri(), + "http://localhost:8000/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC" + ); + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/nft-test/special/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/nft-test/special").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!(req.uri(), EXPECTED_URI); + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!(req.uri(), EXPECTED_URI); + } +} + +pub(crate) mod post { + use crate::address_status::{AddressStatus, AddressStatusOperations}; + use crate::ctx::{AppConfig, ProxyRoute}; + use crate::db::Db; + use crate::http::{ + insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, + }; + use crate::proxy::remove_hop_by_hop_headers; + use crate::rate_limiter::RateLimitOperations; + use crate::rpc::RpcPayload; + use crate::{log_format, GenericResult}; + use hyper::header::{HeaderName, HeaderValue}; + use hyper::{header, Body, Request, Response, StatusCode, Uri}; + use hyper_tls::HttpsConnector; + use proxy_signature::ProxySign; + use std::net::SocketAddr; + + pub(crate) async fn proxy( + cfg: &AppConfig, + mut req: Request, + remote_addr: &SocketAddr, + payload: RpcPayload, + proxy_sign: ProxySign, + x_forwarded_for: HeaderValue, + proxy_route: &ProxyRoute, + ) -> GenericResult> { + // If `allowed_rpc_methods` has values, only those are allowed then. + if !proxy_route.allowed_rpc_methods.is_empty() + && !proxy_route.allowed_rpc_methods.contains(&payload.method) + { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req.uri(), + "Method {} not allowed for, returning 403.", + payload.method + ) + ); + return response_by_status(StatusCode::FORBIDDEN); + } + + if proxy_route.authorized { + // modify outgoing request + if insert_jwt_to_http_header(cfg, req.headers_mut()) + .await + .is_err() + { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req.uri(), + "Error inserting JWT into http header, returning 500." + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let original_req_uri = req.uri().clone(); + *req.uri_mut() = match proxy_route.outbound_route.parse() { + Ok(uri) => uri, + Err(e) => { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + original_req_uri, + "Error type casting value of {} into Uri: {}, returning 500.", + proxy_route.outbound_route, + e + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + }; + + remove_hop_by_hop_headers(&mut req, &[])?; + + req.headers_mut() + .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); + req.headers_mut() + .insert(header::CONTENT_TYPE, APPLICATION_JSON.parse()?); + + let https = HttpsConnector::new(); + let client = hyper::Client::builder().build(https); + + let target_uri = req.uri().clone(); + let res = match client.request(req).await { + Ok(t) => t, + Err(e) => { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + original_req_uri, + "Couldn't reach {}: {}. Returning 503.", + target_uri, + e + ) + ); + return response_by_status(StatusCode::SERVICE_UNAVAILABLE); + } + }; + + Ok(res) + } + + pub(crate) async fn validation_middleware( + cfg: &AppConfig, + proxy_sign: &ProxySign, + proxy_route: &ProxyRoute, + req_uri: &Uri, + remote_addr: &SocketAddr, + ) -> Result<(), StatusCode> { + let mut db = Db::create_instance(cfg).await; + + match db.read_address_status(&proxy_sign.address).await { + AddressStatus::Trusted => Ok(()), + AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), + AddressStatus::None => { + if !proxy_sign.is_valid_message() { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req_uri, + "Request has invalid signed message, returning 401" + ) + ); + + return Err(StatusCode::UNAUTHORIZED); + }; + + let rate_limiter_key = + format!("{}:{}", proxy_route.inbound_route, proxy_sign.address); + + let rate_limiter = proxy_route + .rate_limiter + .as_ref() + .unwrap_or(&cfg.rate_limiter); + match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { + Ok(false) => {} + _ => { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req_uri, + "Rate exceed for {}, checking balance for {} address.", + rate_limiter_key, + proxy_sign.address, + ) + ); + + if !proxy_sign.is_valid_message() { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req_uri, + "Node '{}' sent invalid signed message to inbound '{}', returning 401.", + proxy_sign.address, + proxy_route.inbound_route + ) + ); + + return Err(StatusCode::UNAUTHORIZED); + } + } + }; + + if db.rate_address(rate_limiter_key).await.is_err() { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req_uri, + "Rate incrementing failed." + ) + ); + }; + + Ok(()) + } + } + } +} diff --git a/src/proxy/mod.rs b/src/proxy/mod.rs index 87ce71d..e7cf661 100644 --- a/src/proxy/mod.rs +++ b/src/proxy/mod.rs @@ -1,4 +1,5 @@ use crate::ctx::{AppConfig, GenericResult, ProxyRoute}; +use crate::rpc::RpcPayload; use hyper::header; use hyper::header::{HeaderName, HeaderValue}; use hyper::{Body, Request, Response, StatusCode, Uri}; @@ -6,12 +7,9 @@ use proxy_signature::ProxySign; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::net::SocketAddr; -mod moralis; -use moralis::{proxy_moralis, validation_middleware_moralis}; -mod quicknode; -pub(crate) use quicknode::{ - proxy_quicknode, validation_middleware_quicknode, QuicknodePayload, QuicknodeSocketPayload, -}; + +pub(crate) mod http; +pub(crate) mod websocket; const X_AUTH_PAYLOAD: &str = "X-Auth-Payload"; const KEEP_ALIVE: &str = "keep-alive"; @@ -24,6 +22,7 @@ const KEEP_ALIVE: &str = "keep-alive"; pub(crate) enum ProxyType { Quicknode, Moralis, + Cosmos, } /// Represents the types of payloads that can be processed by the proxy, with each variant tailored to a specific proxy type. @@ -32,19 +31,25 @@ pub(crate) enum ProxyType { pub(crate) enum PayloadData { /// Quicknode feature requires body payload and Signed Message in X-Auth-Payload header Quicknode { - payload: QuicknodePayload, - signed_message: ProxySign, + payload: RpcPayload, + proxy_sign: ProxySign, }, /// Moralis feature requires only Signed Message in X-Auth-Payload header and doesn't have body Moralis(ProxySign), + /// Cosmos feature requires body payload and Signed Message in X-Auth-Payload header + Cosmos { + payload: RpcPayload, + proxy_sign: ProxySign, + }, } impl PayloadData { /// Returns a reference to the `ProxySign` contained within the payload. - pub(crate) fn signed_message(&self) -> &ProxySign { + pub(crate) fn proxy_sign(&self) -> &ProxySign { match self { - PayloadData::Quicknode { signed_message, .. } => signed_message, - PayloadData::Moralis(signed_message) => signed_message, + PayloadData::Quicknode { proxy_sign, .. } => proxy_sign, + PayloadData::Moralis(proxy_sign) => proxy_sign, + PayloadData::Cosmos { proxy_sign, .. } => proxy_sign, } } } @@ -58,17 +63,24 @@ pub(crate) async fn generate_payload_from_req( ) -> GenericResult<(Request, PayloadData)> { match proxy_type { ProxyType::Quicknode => { - let (req, payload, signed_message) = - parse_body_and_auth_header::(req).await?; + let (req, payload, proxy_sign) = parse_body_and_auth_header::(req).await?; let payload_data = PayloadData::Quicknode { payload, - signed_message, + proxy_sign, }; Ok((req, payload_data)) } ProxyType::Moralis => { - let (req, signed_message) = parse_auth_header(req).await?; - Ok((req, PayloadData::Moralis(signed_message))) + let (req, proxy_sign) = parse_auth_header(req).await?; + Ok((req, PayloadData::Moralis(proxy_sign))) + } + ProxyType::Cosmos => { + let (req, payload, proxy_sign) = parse_body_and_auth_header::(req).await?; + let payload_data = PayloadData::Cosmos { + payload, + proxy_sign, + }; + Ok((req, payload_data)) } } } @@ -84,25 +96,40 @@ pub(crate) async fn proxy( match payload { PayloadData::Quicknode { payload, - signed_message, + proxy_sign, } => { - proxy_quicknode( + http::post::proxy( cfg, req, remote_addr, payload, - signed_message, + proxy_sign, x_forwarded_for, proxy_route, ) .await } - PayloadData::Moralis(signed_message) => { - proxy_moralis( + PayloadData::Moralis(proxy_sign) => { + http::get::proxy( cfg, req, remote_addr, - signed_message, + proxy_sign, + x_forwarded_for, + proxy_route, + ) + .await + } + PayloadData::Cosmos { + payload, + proxy_sign, + } => { + http::post::proxy( + cfg, + req, + remote_addr, + payload, + proxy_sign, x_forwarded_for, proxy_route, ) @@ -119,12 +146,16 @@ pub(crate) async fn validation_middleware( remote_addr: &SocketAddr, ) -> Result<(), StatusCode> { match payload { - PayloadData::Quicknode { signed_message, .. } => { - validation_middleware_quicknode(cfg, signed_message, proxy_route, req_uri, remote_addr) + PayloadData::Quicknode { proxy_sign, .. } => { + http::post::validation_middleware(cfg, proxy_sign, proxy_route, req_uri, remote_addr) + .await + } + PayloadData::Moralis(proxy_sign) => { + http::get::validation_middleware(cfg, proxy_sign, proxy_route, req_uri, remote_addr) .await } - PayloadData::Moralis(signed_message) => { - validation_middleware_moralis(cfg, signed_message, proxy_route, req_uri, remote_addr) + PayloadData::Cosmos { proxy_sign, .. } => { + http::post::validation_middleware(cfg, proxy_sign, proxy_route, req_uri, remote_addr) .await } } @@ -147,14 +178,20 @@ where .get(X_AUTH_PAYLOAD) .ok_or("Missing X-Auth-Payload header")? .to_str()?; - let signed_message: ProxySign = serde_json::from_str(header_value)?; + println!("HEADER VALUE {:?}", header_value); + let proxy_sign: ProxySign = serde_json::from_str(header_value)?; + println!("HEADER VALUE {:?}", header_value); let body_bytes = hyper::body::to_bytes(body).await?; if body_bytes.is_empty() { return Err("Empty body cannot be deserialized into non-optional type T".into()); } + let payload: serde_json::Value = serde_json::from_slice(&body_bytes)?; + println!("AAAAAAAAAAA {:?}", payload); let payload: T = serde_json::from_slice(&body_bytes)?; + println!("AAAAAAAAAAAAAAAAAAAAAAAA"); let new_req = Request::from_parts(parts, Body::from(body_bytes)); - Ok((new_req, payload, signed_message)) + println!("WOOOOOOOOOOOOOOO"); + Ok((new_req, payload, proxy_sign)) } /// Parses [ProxySign] value from X-Auth-Payload header diff --git a/src/proxy/moralis.rs b/src/proxy/moralis.rs deleted file mode 100644 index 74a6508..0000000 --- a/src/proxy/moralis.rs +++ /dev/null @@ -1,304 +0,0 @@ -use crate::address_status::{AddressStatus, AddressStatusOperations}; -use crate::ctx::{AppConfig, ProxyRoute}; -use crate::db::Db; -use crate::http::{ - insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, -}; -use crate::proxy::remove_hop_by_hop_headers; -use crate::rate_limiter::RateLimitOperations; -use crate::{log_format, GenericResult}; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::{header, Body, Request, Response, StatusCode, Uri}; -use hyper_tls::HttpsConnector; -use proxy_signature::ProxySign; -use std::net::SocketAddr; - -pub(crate) async fn proxy_moralis( - cfg: &AppConfig, - mut req: Request, - remote_addr: &SocketAddr, - signed_message: ProxySign, - x_forwarded_for: HeaderValue, - proxy_route: &ProxyRoute, -) -> GenericResult> { - if proxy_route.authorized { - if let Err(e) = insert_jwt_to_http_header(cfg, req.headers_mut()).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req.uri(), - "Error inserting JWT into HTTP header: {}, returning 500.", - e - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - } - - let original_req_uri = req.uri().clone(); - - if let Err(e) = modify_request_uri(&mut req, proxy_route) { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - original_req_uri, - "Error modifying request base Uri: {}, returning 500.", - e - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - - remove_hop_by_hop_headers(&mut req, &[header::CONTENT_LENGTH])?; - - req.headers_mut() - .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); - req.headers_mut() - .insert(header::ACCEPT, APPLICATION_JSON.parse()?); - - let https = HttpsConnector::new(); - let client = hyper::Client::builder().build(https); - - let target_uri = req.uri().clone(); - let res = match client.request(req).await { - Ok(t) => t, - Err(e) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - original_req_uri, - "Couldn't reach {}: {}. Returning 503.", - target_uri, - e - ) - ); - return response_by_status(StatusCode::SERVICE_UNAVAILABLE); - } - }; - - Ok(res) -} - -/// This function removes the matched inbound route from the request URI and -/// replaces request base URI with the outbound route specified in the proxy route. -fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> GenericResult<()> { - let proxy_base_uri = proxy_route.outbound_route.parse::()?; - let original_uri = req.uri(); - - let original_path_and_query = original_uri - .path_and_query() - .map(|pq| pq.as_str()) - .unwrap_or(""); - // Remove the "inbound_route" part from the original path and query - let remaining_path_and_query = if proxy_route.inbound_route == "/" { - original_path_and_query - } else { - original_path_and_query - .strip_prefix(&proxy_route.inbound_route) - .ok_or("Route doesn't match with the given inbound URL.")? - }; - - let mut base_uri_parts = proxy_base_uri.into_parts(); - base_uri_parts.path_and_query = Some(remaining_path_and_query.parse()?); - let new_uri = Uri::from_parts(base_uri_parts)?; - *req.uri_mut() = new_uri; - Ok(()) -} - -pub(crate) async fn validation_middleware_moralis( - cfg: &AppConfig, - signed_message: &ProxySign, - proxy_route: &ProxyRoute, - req_uri: &Uri, - remote_addr: &SocketAddr, -) -> Result<(), StatusCode> { - let mut db = Db::create_instance(cfg).await; - - match db.read_address_status(&signed_message.address).await { - AddressStatus::Trusted => Ok(()), - AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), - AddressStatus::None => { - if !signed_message.is_valid_message() { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Request has invalid signed message, returning 401" - ) - ); - - return Err(StatusCode::UNAUTHORIZED); - } - - let rate_limiter_key = - format!("{}:{}", proxy_route.inbound_route, signed_message.address); - - let rate_limiter = proxy_route - .rate_limiter - .as_ref() - .unwrap_or(&cfg.rate_limiter); - match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { - Ok(false) => {} - Ok(true) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate exceed for {}, returning 406.", - rate_limiter_key, - ) - ); - return Err(StatusCode::NOT_ACCEPTABLE); - } - Err(e) => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate exceeded check failed for node '{}': {}, returning 500.", - signed_message.address, - e - ) - ); - return Err(StatusCode::INTERNAL_SERVER_ERROR); - } - } - - if let Err(e) = db.rate_address(rate_limiter_key).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate incrementing failed for node '{}': {}, returning 500.", - signed_message.address, - e - ) - ); - return Err(StatusCode::INTERNAL_SERVER_ERROR); - }; - - Ok(()) - } - } -} - -#[tokio::test] -async fn test_parse_moralis_payload() { - use super::{parse_auth_header, X_AUTH_PAYLOAD}; - use hyper::header::HeaderName; - use hyper::Method; - - let serialized_payload = serde_json::json!({ - "coin_ticker": "BTC", - "address": "dummy-value", - "timestamp_message": 1655320000, - "signature": "dummy-value", - }) - .to_string(); - - let req = Request::builder() - .method(Method::GET) - .header(header::ACCEPT, HeaderValue::from_static(APPLICATION_JSON)) - .header( - X_AUTH_PAYLOAD, - HeaderValue::from_str(&serialized_payload).unwrap(), - ) - .body(Body::empty()) - .unwrap(); - - let (mut req, payload) = parse_auth_header(req).await.unwrap(); - - let body_bytes = hyper::body::to_bytes(req.body_mut()).await.unwrap(); - assert!( - body_bytes.is_empty(), - "Body should be empty for GET methods" - ); - - let header_value = req.headers().get(header::ACCEPT).unwrap(); - - // let expected_payload = SignedMessage { - // coin_ticker: String::from("BTC"), - // address: String::from("dummy-value"), - // timestamp_message: 1655320000, - // signature: String::from("dummy-value"), - // }; - - // assert_eq!(payload, expected_payload); - // assert_eq!(header_value, APPLICATION_JSON); - - // let additional_headers = &[ - // header::CONTENT_LENGTH, - // HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), - // ]; - // remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); -} - -#[tokio::test] -async fn test_modify_request_uri() { - use super::ProxyType; - use std::str::FromStr; - - const EXPECTED_URI: &str = "http://localhost:8000/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC"; - - let mut req = Request::builder() - .uri("https://komodo.proxy:5535/nft-test/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") - .body(Body::empty()) - .unwrap(); - let proxy_route = ProxyRoute { - inbound_route: String::from_str("/nft-test").unwrap(), - outbound_route: "http://localhost:8000".to_string(), - proxy_type: ProxyType::Moralis, - authorized: false, - allowed_rpc_methods: vec![], - rate_limiter: None, - }; - modify_request_uri(&mut req, &proxy_route).unwrap(); - assert_eq!( - req.uri(), - "http://localhost:8000/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC" - ); - - let mut req = Request::builder() - .uri("https://komodo.proxy:5535/nft-test/special/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") - .body(Body::empty()) - .unwrap(); - let proxy_route = ProxyRoute { - inbound_route: String::from_str("/nft-test/special").unwrap(), - outbound_route: "http://localhost:8000".to_string(), - proxy_type: ProxyType::Moralis, - authorized: false, - allowed_rpc_methods: vec![], - rate_limiter: None, - }; - modify_request_uri(&mut req, &proxy_route).unwrap(); - assert_eq!(req.uri(), EXPECTED_URI); - - let mut req = Request::builder() - .uri("https://komodo.proxy:5535/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") - .body(Body::empty()) - .unwrap(); - let proxy_route = ProxyRoute { - inbound_route: String::from_str("/").unwrap(), - outbound_route: "http://localhost:8000".to_string(), - proxy_type: ProxyType::Moralis, - authorized: false, - allowed_rpc_methods: vec![], - rate_limiter: None, - }; - modify_request_uri(&mut req, &proxy_route).unwrap(); - assert_eq!(req.uri(), EXPECTED_URI); -} diff --git a/src/proxy/quicknode.rs b/src/proxy/quicknode.rs deleted file mode 100644 index 947b8ab..0000000 --- a/src/proxy/quicknode.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::address_status::{AddressStatus, AddressStatusOperations}; -use crate::ctx::{AppConfig, ProxyRoute}; -use crate::db::Db; -use crate::http::{ - insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, -}; -use crate::proxy::remove_hop_by_hop_headers; -use crate::rate_limiter::RateLimitOperations; -use crate::{log_format, GenericResult}; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::{header, Body, Request, Response, StatusCode, Uri}; -use hyper_tls::HttpsConnector; -use proxy_signature::ProxySign; -use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; - -/// Represents a payload for JSON-RPC calls, tailored for the Quicknode API within the proxy. -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub(crate) struct QuicknodePayload { - pub(crate) method: String, - pub(crate) params: serde_json::value::Value, - pub(crate) id: usize, - pub(crate) jsonrpc: String, -} - -/// Used for websocket connection. -/// It combines standard JSON RPC method call fields (method, params, id, jsonrpc) with a `SignedMessage` -/// for authentication and validation, facilitating secure and validated interactions with the Quicknode service. -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub(crate) struct QuicknodeSocketPayload { - pub(crate) method: String, - pub(crate) params: serde_json::value::Value, - pub(crate) id: usize, - pub(crate) jsonrpc: String, - pub(crate) signed_message: ProxySign, -} - -impl QuicknodeSocketPayload { - pub(crate) fn into_parts(self) -> (QuicknodePayload, ProxySign) { - let payload = QuicknodePayload { - method: self.method, - params: self.params, - id: self.id, - jsonrpc: self.jsonrpc, - }; - let signed_message = self.signed_message; - (payload, signed_message) - } -} - -#[derive(Debug)] -enum ProofOfFundingError { - InvalidSignedMessage, - InsufficientBalance, - ErrorFromRpcCall, - #[allow(dead_code)] - RpcCallFailed(String), -} - -pub(crate) async fn proxy_quicknode( - cfg: &AppConfig, - mut req: Request, - remote_addr: &SocketAddr, - payload: QuicknodePayload, - signed_message: ProxySign, - x_forwarded_for: HeaderValue, - proxy_route: &ProxyRoute, -) -> GenericResult> { - // check if requested method allowed - if !proxy_route.allowed_rpc_methods.contains(&payload.method) { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req.uri(), - "Method {} not allowed for, returning 403.", - payload.method - ) - ); - return response_by_status(StatusCode::FORBIDDEN); - } - - if proxy_route.authorized { - // modify outgoing request - if insert_jwt_to_http_header(cfg, req.headers_mut()) - .await - .is_err() - { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req.uri(), - "Error inserting JWT into http header, returning 500." - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - } - - let original_req_uri = req.uri().clone(); - *req.uri_mut() = match proxy_route.outbound_route.parse() { - Ok(uri) => uri, - Err(e) => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - original_req_uri, - "Error type casting value of {} into Uri: {}, returning 500.", - proxy_route.outbound_route, - e - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - }; - - remove_hop_by_hop_headers(&mut req, &[])?; - - req.headers_mut() - .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); - req.headers_mut() - .insert(header::CONTENT_TYPE, APPLICATION_JSON.parse()?); - - let https = HttpsConnector::new(); - let client = hyper::Client::builder().build(https); - - let target_uri = req.uri().clone(); - let res = match client.request(req).await { - Ok(t) => t, - Err(e) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - original_req_uri, - "Couldn't reach {}: {}. Returning 503.", - target_uri, - e - ) - ); - return response_by_status(StatusCode::SERVICE_UNAVAILABLE); - } - }; - - Ok(res) -} - -pub(crate) async fn validation_middleware_quicknode( - cfg: &AppConfig, - signed_message: &ProxySign, - proxy_route: &ProxyRoute, - req_uri: &Uri, - remote_addr: &SocketAddr, -) -> Result<(), StatusCode> { - let mut db = Db::create_instance(cfg).await; - - match db.read_address_status(&signed_message.address).await { - AddressStatus::Trusted => Ok(()), - AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), - AddressStatus::None => { - if !signed_message.is_valid_message() { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Request has invalid signed message, returning 401" - ) - ); - - return Err(StatusCode::UNAUTHORIZED); - }; - - let rate_limiter_key = - format!("{}:{}", proxy_route.inbound_route, signed_message.address); - - let rate_limiter = proxy_route - .rate_limiter - .as_ref() - .unwrap_or(&cfg.rate_limiter); - match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { - Ok(false) => {} - _ => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate exceed for {}, checking balance for {} address.", - rate_limiter_key, - signed_message.address, - ) - ); - - if !signed_message.is_valid_message() { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Node '{}' sent invalid signed message to inbound '{}', returning 401.", - signed_message.address, - proxy_route.inbound_route - ) - ); - - return Err(StatusCode::UNAUTHORIZED); - } - } - }; - - if db.rate_address(rate_limiter_key).await.is_err() { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate incrementing failed." - ) - ); - }; - - Ok(()) - } - } -} diff --git a/src/net/websocket.rs b/src/proxy/websocket.rs similarity index 96% rename from src/net/websocket.rs rename to src/proxy/websocket.rs index 2b22150..b0b2af1 100644 --- a/src/net/websocket.rs +++ b/src/proxy/websocket.rs @@ -9,11 +9,7 @@ use tokio_tungstenite::{ }; use crate::{ - ctx::AppConfig, - http::response_by_status, - log_format, - proxy::{validation_middleware_quicknode, QuicknodeSocketPayload}, - GenericResult, + ctx::AppConfig, http::response_by_status, log_format, rpc::RpcSocketPayload, GenericResult, }; pub(crate) fn should_upgrade_to_socket_conn(req: &Request) -> bool { @@ -137,7 +133,7 @@ pub(crate) async fn socket_handler( match msg { Some(Ok(msg)) => { if let Message::Text(msg) = msg { - let socket_payload: QuicknodeSocketPayload = match serde_json::from_str(&msg) { + let socket_payload: RpcSocketPayload = match serde_json::from_str(&msg) { Ok(t) => t, Err(e) => { if let Err(e) = inbound_socket.send(format!("Invalid payload. {e}").into()).await { @@ -173,8 +169,7 @@ pub(crate) async fn socket_handler( continue; } - // TODO add general validation_middleware support (if have new features which support websocket) - match validation_middleware_quicknode( + match crate::proxy::http::post::validation_middleware( &cfg, &signed_message, &proxy_route, From c7a964f6dc1b893434119fafe74b80bbb9c707db Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 08:35:12 +0300 Subject: [PATCH 09/23] unify middleware validation Signed-off-by: onur-ozkan --- src/proxy/http/get.rs | 223 ++++++++++++++++++ src/proxy/http/mod.rs | 515 +++++------------------------------------ src/proxy/http/post.rs | 108 +++++++++ src/proxy/mod.rs | 16 +- src/proxy/websocket.rs | 2 +- 5 files changed, 389 insertions(+), 475 deletions(-) create mode 100644 src/proxy/http/get.rs create mode 100644 src/proxy/http/post.rs diff --git a/src/proxy/http/get.rs b/src/proxy/http/get.rs new file mode 100644 index 0000000..5a5272f --- /dev/null +++ b/src/proxy/http/get.rs @@ -0,0 +1,223 @@ +use crate::address_status::{AddressStatus, AddressStatusOperations}; +use crate::ctx::{AppConfig, ProxyRoute}; +use crate::db::Db; +use crate::http::{ + insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, +}; +use crate::proxy::remove_hop_by_hop_headers; +use crate::rate_limiter::RateLimitOperations; +use crate::{log_format, GenericResult}; +use hyper::header::{HeaderName, HeaderValue}; +use hyper::{header, Body, Request, Response, StatusCode, Uri}; +use hyper_tls::HttpsConnector; +use proxy_signature::ProxySign; +use std::net::SocketAddr; + +pub(crate) async fn proxy( + cfg: &AppConfig, + mut req: Request, + remote_addr: &SocketAddr, + signed_message: ProxySign, + x_forwarded_for: HeaderValue, + proxy_route: &ProxyRoute, +) -> GenericResult> { + if proxy_route.authorized { + if let Err(e) = insert_jwt_to_http_header(cfg, req.headers_mut()).await { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + req.uri(), + "Error inserting JWT into HTTP header: {}, returning 500.", + e + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let original_req_uri = req.uri().clone(); + + if let Err(e) = modify_request_uri(&mut req, proxy_route) { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + original_req_uri, + "Error modifying request base Uri: {}, returning 500.", + e + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + + remove_hop_by_hop_headers(&mut req, &[header::CONTENT_LENGTH])?; + + req.headers_mut() + .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); + req.headers_mut() + .insert(header::ACCEPT, APPLICATION_JSON.parse()?); + + let https = HttpsConnector::new(); + let client = hyper::Client::builder().build(https); + + let target_uri = req.uri().clone(); + let res = match client.request(req).await { + Ok(t) => t, + Err(e) => { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + signed_message.address, + original_req_uri, + "Couldn't reach {}: {}. Returning 503.", + target_uri, + e + ) + ); + return response_by_status(StatusCode::SERVICE_UNAVAILABLE); + } + }; + + Ok(res) +} + +/// This function removes the matched inbound route from the request URI and +/// replaces request base URI with the outbound route specified in the proxy route. +fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> GenericResult<()> { + let proxy_base_uri = proxy_route.outbound_route.parse::()?; + let original_uri = req.uri(); + + let original_path_and_query = original_uri + .path_and_query() + .map(|pq| pq.as_str()) + .unwrap_or(""); + // Remove the "inbound_route" part from the original path and query + let remaining_path_and_query = if proxy_route.inbound_route == "/" { + original_path_and_query + } else { + original_path_and_query + .strip_prefix(&proxy_route.inbound_route) + .ok_or("Route doesn't match with the given inbound URL.")? + }; + + let mut base_uri_parts = proxy_base_uri.into_parts(); + base_uri_parts.path_and_query = Some(remaining_path_and_query.parse()?); + let new_uri = Uri::from_parts(base_uri_parts)?; + *req.uri_mut() = new_uri; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_parse_payload() { + // use hyper::header::HeaderName; + use hyper::Method; + + let serialized_payload = serde_json::json!({ + "coin_ticker": "BTC", + "address": "dummy-value", + "timestamp_message": 1655320000, + "signature": "dummy-value", + }) + .to_string(); + + let req = Request::builder() + .method(Method::GET) + .header(header::ACCEPT, HeaderValue::from_static(APPLICATION_JSON)) + .header( + crate::proxy::X_AUTH_PAYLOAD, + HeaderValue::from_str(&serialized_payload).unwrap(), + ) + .body(Body::empty()) + .unwrap(); + + let (mut req, payload) = crate::proxy::parse_auth_header(req).await.unwrap(); + + let body_bytes = hyper::body::to_bytes(req.body_mut()).await.unwrap(); + assert!( + body_bytes.is_empty(), + "Body should be empty for GET methods" + ); + + let header_value = req.headers().get(header::ACCEPT).unwrap(); + + // let expected_payload = SignedMessage { + // coin_ticker: String::from("BTC"), + // address: String::from("dummy-value"), + // timestamp_message: 1655320000, + // signature: String::from("dummy-value"), + // }; + + // assert_eq!(payload, expected_payload); + // assert_eq!(header_value, APPLICATION_JSON); + + // let additional_headers = &[ + // header::CONTENT_LENGTH, + // HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), + // ]; + // remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); + } + + #[tokio::test] + async fn test_modify_request_uri() { + use crate::proxy::ProxyType; + use std::str::FromStr; + + const EXPECTED_URI: &str = "http://localhost:8000/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC"; + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/nft-test/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/nft-test").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!( + req.uri(), + "http://localhost:8000/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC" + ); + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/nft-test/special/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/nft-test/special").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!(req.uri(), EXPECTED_URI); + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!(req.uri(), EXPECTED_URI); + } +} diff --git a/src/proxy/http/mod.rs b/src/proxy/http/mod.rs index 0ef1557..5ae06de 100644 --- a/src/proxy/http/mod.rs +++ b/src/proxy/http/mod.rs @@ -1,503 +1,98 @@ -pub(crate) mod get { - use crate::address_status::{AddressStatus, AddressStatusOperations}; - use crate::ctx::{AppConfig, ProxyRoute}; - use crate::db::Db; - use crate::http::{ - insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, - }; - use crate::proxy::remove_hop_by_hop_headers; - use crate::rate_limiter::RateLimitOperations; - use crate::{log_format, GenericResult}; - use hyper::header::{HeaderName, HeaderValue}; - use hyper::{header, Body, Request, Response, StatusCode, Uri}; - use hyper_tls::HttpsConnector; - use proxy_signature::ProxySign; - use std::net::SocketAddr; - - pub(crate) async fn proxy( - cfg: &AppConfig, - mut req: Request, - remote_addr: &SocketAddr, - signed_message: ProxySign, - x_forwarded_for: HeaderValue, - proxy_route: &ProxyRoute, - ) -> GenericResult> { - if proxy_route.authorized { - if let Err(e) = insert_jwt_to_http_header(cfg, req.headers_mut()).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req.uri(), - "Error inserting JWT into HTTP header: {}, returning 500.", - e - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - } - - let original_req_uri = req.uri().clone(); - - if let Err(e) = modify_request_uri(&mut req, proxy_route) { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - original_req_uri, - "Error modifying request base Uri: {}, returning 500.", - e - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - - remove_hop_by_hop_headers(&mut req, &[header::CONTENT_LENGTH])?; - - req.headers_mut() - .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); - req.headers_mut() - .insert(header::ACCEPT, APPLICATION_JSON.parse()?); - - let https = HttpsConnector::new(); - let client = hyper::Client::builder().build(https); - - let target_uri = req.uri().clone(); - let res = match client.request(req).await { - Ok(t) => t, - Err(e) => { +use std::net::SocketAddr; + +use hyper::{StatusCode, Uri}; +use proxy_signature::ProxySign; + +use crate::{ + address_status::{AddressStatus, AddressStatusOperations}, + ctx::{AppConfig, ProxyRoute}, + db::Db, log_format, rate_limiter::RateLimitOperations, +}; + +pub(crate) mod get; +pub(crate) mod post; + +pub(crate) async fn validation_middleware( + cfg: &AppConfig, + signed_message: &ProxySign, + proxy_route: &ProxyRoute, + req_uri: &Uri, + remote_addr: &SocketAddr, +) -> Result<(), StatusCode> { + let mut db = Db::create_instance(cfg).await; + + match db.read_address_status(&signed_message.address).await { + AddressStatus::Trusted => Ok(()), + AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), + AddressStatus::None => { + if !signed_message.is_valid_message() { log::warn!( "{}", log_format!( remote_addr.ip(), signed_message.address, - original_req_uri, - "Couldn't reach {}: {}. Returning 503.", - target_uri, - e + req_uri, + "Request has invalid signed message, returning 401" ) ); - return response_by_status(StatusCode::SERVICE_UNAVAILABLE); - } - }; - Ok(res) - } - - /// This function removes the matched inbound route from the request URI and - /// replaces request base URI with the outbound route specified in the proxy route. - fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> GenericResult<()> { - let proxy_base_uri = proxy_route.outbound_route.parse::()?; - let original_uri = req.uri(); - - let original_path_and_query = original_uri - .path_and_query() - .map(|pq| pq.as_str()) - .unwrap_or(""); - // Remove the "inbound_route" part from the original path and query - let remaining_path_and_query = if proxy_route.inbound_route == "/" { - original_path_and_query - } else { - original_path_and_query - .strip_prefix(&proxy_route.inbound_route) - .ok_or("Route doesn't match with the given inbound URL.")? - }; - - let mut base_uri_parts = proxy_base_uri.into_parts(); - base_uri_parts.path_and_query = Some(remaining_path_and_query.parse()?); - let new_uri = Uri::from_parts(base_uri_parts)?; - *req.uri_mut() = new_uri; - Ok(()) - } + return Err(StatusCode::UNAUTHORIZED); + } - pub(crate) async fn validation_middleware( - cfg: &AppConfig, - signed_message: &ProxySign, - proxy_route: &ProxyRoute, - req_uri: &Uri, - remote_addr: &SocketAddr, - ) -> Result<(), StatusCode> { - let mut db = Db::create_instance(cfg).await; + let rate_limiter_key = + format!("{}:{}", proxy_route.inbound_route, signed_message.address); - match db.read_address_status(&signed_message.address).await { - AddressStatus::Trusted => Ok(()), - AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), - AddressStatus::None => { - if !signed_message.is_valid_message() { + let rate_limiter = proxy_route + .rate_limiter + .as_ref() + .unwrap_or(&cfg.rate_limiter); + match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { + Ok(false) => {} + Ok(true) => { log::warn!( "{}", log_format!( remote_addr.ip(), signed_message.address, req_uri, - "Request has invalid signed message, returning 401" + "Rate exceed for {}, returning 406.", + rate_limiter_key, ) ); - - return Err(StatusCode::UNAUTHORIZED); - } - - let rate_limiter_key = - format!("{}:{}", proxy_route.inbound_route, signed_message.address); - - let rate_limiter = proxy_route - .rate_limiter - .as_ref() - .unwrap_or(&cfg.rate_limiter); - match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { - Ok(false) => {} - Ok(true) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate exceed for {}, returning 406.", - rate_limiter_key, - ) - ); - return Err(StatusCode::NOT_ACCEPTABLE); - } - Err(e) => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate exceeded check failed for node '{}': {}, returning 500.", - signed_message.address, - e - ) - ); - return Err(StatusCode::INTERNAL_SERVER_ERROR); - } + return Err(StatusCode::NOT_ACCEPTABLE); } - - if let Err(e) = db.rate_address(rate_limiter_key).await { + Err(e) => { log::error!( "{}", log_format!( remote_addr.ip(), signed_message.address, req_uri, - "Rate incrementing failed for node '{}': {}, returning 500.", + "Rate exceeded check failed for node '{}': {}, returning 500.", signed_message.address, e ) ); return Err(StatusCode::INTERNAL_SERVER_ERROR); - }; - - Ok(()) - } - } - } - - #[tokio::test] - async fn test_parse_payload() { - // use hyper::header::HeaderName; - use hyper::Method; - - let serialized_payload = serde_json::json!({ - "coin_ticker": "BTC", - "address": "dummy-value", - "timestamp_message": 1655320000, - "signature": "dummy-value", - }) - .to_string(); - - let req = Request::builder() - .method(Method::GET) - .header(header::ACCEPT, HeaderValue::from_static(APPLICATION_JSON)) - .header( - crate::proxy::X_AUTH_PAYLOAD, - HeaderValue::from_str(&serialized_payload).unwrap(), - ) - .body(Body::empty()) - .unwrap(); - - let (mut req, payload) = crate::proxy::parse_auth_header(req).await.unwrap(); - - let body_bytes = hyper::body::to_bytes(req.body_mut()).await.unwrap(); - assert!( - body_bytes.is_empty(), - "Body should be empty for GET methods" - ); - - let header_value = req.headers().get(header::ACCEPT).unwrap(); - - // let expected_payload = SignedMessage { - // coin_ticker: String::from("BTC"), - // address: String::from("dummy-value"), - // timestamp_message: 1655320000, - // signature: String::from("dummy-value"), - // }; - - // assert_eq!(payload, expected_payload); - // assert_eq!(header_value, APPLICATION_JSON); - - // let additional_headers = &[ - // header::CONTENT_LENGTH, - // HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), - // ]; - // remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); - } - - #[tokio::test] - async fn test_modify_request_uri() { - use std::str::FromStr; - use crate::proxy::ProxyType; - - const EXPECTED_URI: &str = "http://localhost:8000/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC"; - - let mut req = Request::builder() - .uri("https://komodo.proxy:5535/nft-test/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") - .body(Body::empty()) - .unwrap(); - let proxy_route = ProxyRoute { - inbound_route: String::from_str("/nft-test").unwrap(), - outbound_route: "http://localhost:8000".to_string(), - proxy_type: ProxyType::Moralis, - authorized: false, - allowed_rpc_methods: vec![], - rate_limiter: None, - }; - modify_request_uri(&mut req, &proxy_route).unwrap(); - assert_eq!( - req.uri(), - "http://localhost:8000/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC" - ); - - let mut req = Request::builder() - .uri("https://komodo.proxy:5535/nft-test/special/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") - .body(Body::empty()) - .unwrap(); - let proxy_route = ProxyRoute { - inbound_route: String::from_str("/nft-test/special").unwrap(), - outbound_route: "http://localhost:8000".to_string(), - proxy_type: ProxyType::Moralis, - authorized: false, - allowed_rpc_methods: vec![], - rate_limiter: None, - }; - modify_request_uri(&mut req, &proxy_route).unwrap(); - assert_eq!(req.uri(), EXPECTED_URI); - - let mut req = Request::builder() - .uri("https://komodo.proxy:5535/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") - .body(Body::empty()) - .unwrap(); - let proxy_route = ProxyRoute { - inbound_route: String::from_str("/").unwrap(), - outbound_route: "http://localhost:8000".to_string(), - proxy_type: ProxyType::Moralis, - authorized: false, - allowed_rpc_methods: vec![], - rate_limiter: None, - }; - modify_request_uri(&mut req, &proxy_route).unwrap(); - assert_eq!(req.uri(), EXPECTED_URI); - } -} - -pub(crate) mod post { - use crate::address_status::{AddressStatus, AddressStatusOperations}; - use crate::ctx::{AppConfig, ProxyRoute}; - use crate::db::Db; - use crate::http::{ - insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, - }; - use crate::proxy::remove_hop_by_hop_headers; - use crate::rate_limiter::RateLimitOperations; - use crate::rpc::RpcPayload; - use crate::{log_format, GenericResult}; - use hyper::header::{HeaderName, HeaderValue}; - use hyper::{header, Body, Request, Response, StatusCode, Uri}; - use hyper_tls::HttpsConnector; - use proxy_signature::ProxySign; - use std::net::SocketAddr; - - pub(crate) async fn proxy( - cfg: &AppConfig, - mut req: Request, - remote_addr: &SocketAddr, - payload: RpcPayload, - proxy_sign: ProxySign, - x_forwarded_for: HeaderValue, - proxy_route: &ProxyRoute, - ) -> GenericResult> { - // If `allowed_rpc_methods` has values, only those are allowed then. - if !proxy_route.allowed_rpc_methods.is_empty() - && !proxy_route.allowed_rpc_methods.contains(&payload.method) - { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req.uri(), - "Method {} not allowed for, returning 403.", - payload.method - ) - ); - return response_by_status(StatusCode::FORBIDDEN); - } - - if proxy_route.authorized { - // modify outgoing request - if insert_jwt_to_http_header(cfg, req.headers_mut()) - .await - .is_err() - { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req.uri(), - "Error inserting JWT into http header, returning 500." - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } } - } - let original_req_uri = req.uri().clone(); - *req.uri_mut() = match proxy_route.outbound_route.parse() { - Ok(uri) => uri, - Err(e) => { + if let Err(e) = db.rate_address(rate_limiter_key).await { log::error!( "{}", log_format!( remote_addr.ip(), - proxy_sign.address, - original_req_uri, - "Error type casting value of {} into Uri: {}, returning 500.", - proxy_route.outbound_route, - e - ) - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - }; - - remove_hop_by_hop_headers(&mut req, &[])?; - - req.headers_mut() - .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); - req.headers_mut() - .insert(header::CONTENT_TYPE, APPLICATION_JSON.parse()?); - - let https = HttpsConnector::new(); - let client = hyper::Client::builder().build(https); - - let target_uri = req.uri().clone(); - let res = match client.request(req).await { - Ok(t) => t, - Err(e) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - original_req_uri, - "Couldn't reach {}: {}. Returning 503.", - target_uri, + signed_message.address, + req_uri, + "Rate incrementing failed for node '{}': {}, returning 500.", + signed_message.address, e ) ); - return response_by_status(StatusCode::SERVICE_UNAVAILABLE); - } - }; - - Ok(res) - } - - pub(crate) async fn validation_middleware( - cfg: &AppConfig, - proxy_sign: &ProxySign, - proxy_route: &ProxyRoute, - req_uri: &Uri, - remote_addr: &SocketAddr, - ) -> Result<(), StatusCode> { - let mut db = Db::create_instance(cfg).await; - - match db.read_address_status(&proxy_sign.address).await { - AddressStatus::Trusted => Ok(()), - AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), - AddressStatus::None => { - if !proxy_sign.is_valid_message() { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req_uri, - "Request has invalid signed message, returning 401" - ) - ); - - return Err(StatusCode::UNAUTHORIZED); - }; + return Err(StatusCode::INTERNAL_SERVER_ERROR); + }; - let rate_limiter_key = - format!("{}:{}", proxy_route.inbound_route, proxy_sign.address); - - let rate_limiter = proxy_route - .rate_limiter - .as_ref() - .unwrap_or(&cfg.rate_limiter); - match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { - Ok(false) => {} - _ => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req_uri, - "Rate exceed for {}, checking balance for {} address.", - rate_limiter_key, - proxy_sign.address, - ) - ); - - if !proxy_sign.is_valid_message() { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req_uri, - "Node '{}' sent invalid signed message to inbound '{}', returning 401.", - proxy_sign.address, - proxy_route.inbound_route - ) - ); - - return Err(StatusCode::UNAUTHORIZED); - } - } - }; - - if db.rate_address(rate_limiter_key).await.is_err() { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req_uri, - "Rate incrementing failed." - ) - ); - }; - - Ok(()) - } + Ok(()) } } } diff --git a/src/proxy/http/post.rs b/src/proxy/http/post.rs new file mode 100644 index 0000000..56f91ec --- /dev/null +++ b/src/proxy/http/post.rs @@ -0,0 +1,108 @@ +use crate::ctx::{AppConfig, ProxyRoute}; +use crate::http::{ + insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, +}; +use crate::proxy::remove_hop_by_hop_headers; +use crate::rpc::RpcPayload; +use crate::{log_format, GenericResult}; +use hyper::header::{HeaderName, HeaderValue}; +use hyper::{header, Body, Request, Response, StatusCode}; +use hyper_tls::HttpsConnector; +use proxy_signature::ProxySign; +use std::net::SocketAddr; + +pub(crate) async fn proxy( + cfg: &AppConfig, + mut req: Request, + remote_addr: &SocketAddr, + payload: RpcPayload, + proxy_sign: ProxySign, + x_forwarded_for: HeaderValue, + proxy_route: &ProxyRoute, +) -> GenericResult> { + // If `allowed_rpc_methods` has values, only those are allowed then. + if !proxy_route.allowed_rpc_methods.is_empty() + && !proxy_route.allowed_rpc_methods.contains(&payload.method) + { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req.uri(), + "Method {} not allowed for, returning 403.", + payload.method + ) + ); + return response_by_status(StatusCode::FORBIDDEN); + } + + if proxy_route.authorized { + // modify outgoing request + if insert_jwt_to_http_header(cfg, req.headers_mut()) + .await + .is_err() + { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + req.uri(), + "Error inserting JWT into http header, returning 500." + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let original_req_uri = req.uri().clone(); + *req.uri_mut() = match proxy_route.outbound_route.parse() { + Ok(uri) => uri, + Err(e) => { + log::error!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + original_req_uri, + "Error type casting value of {} into Uri: {}, returning 500.", + proxy_route.outbound_route, + e + ) + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + }; + + remove_hop_by_hop_headers(&mut req, &[])?; + + req.headers_mut() + .insert(HeaderName::from_static(X_FORWARDED_FOR), x_forwarded_for); + req.headers_mut() + .insert(header::CONTENT_TYPE, APPLICATION_JSON.parse()?); + + let https = HttpsConnector::new(); + let client = hyper::Client::builder().build(https); + + let target_uri = req.uri().clone(); + let res = match client.request(req).await { + Ok(t) => t, + Err(e) => { + log::warn!( + "{}", + log_format!( + remote_addr.ip(), + proxy_sign.address, + original_req_uri, + "Couldn't reach {}: {}. Returning 503.", + target_uri, + e + ) + ); + return response_by_status(StatusCode::SERVICE_UNAVAILABLE); + } + }; + + Ok(res) +} diff --git a/src/proxy/mod.rs b/src/proxy/mod.rs index e7cf661..13b569c 100644 --- a/src/proxy/mod.rs +++ b/src/proxy/mod.rs @@ -145,20 +145,8 @@ pub(crate) async fn validation_middleware( req_uri: &Uri, remote_addr: &SocketAddr, ) -> Result<(), StatusCode> { - match payload { - PayloadData::Quicknode { proxy_sign, .. } => { - http::post::validation_middleware(cfg, proxy_sign, proxy_route, req_uri, remote_addr) - .await - } - PayloadData::Moralis(proxy_sign) => { - http::get::validation_middleware(cfg, proxy_sign, proxy_route, req_uri, remote_addr) - .await - } - PayloadData::Cosmos { proxy_sign, .. } => { - http::post::validation_middleware(cfg, proxy_sign, proxy_route, req_uri, remote_addr) - .await - } - } + let proxy_sign = payload.proxy_sign(); + http::validation_middleware(cfg, proxy_sign, proxy_route, req_uri, remote_addr).await } /// Parses the request body and the `X-Auth-Payload` header into a payload and signed message. diff --git a/src/proxy/websocket.rs b/src/proxy/websocket.rs index b0b2af1..647fd2f 100644 --- a/src/proxy/websocket.rs +++ b/src/proxy/websocket.rs @@ -169,7 +169,7 @@ pub(crate) async fn socket_handler( continue; } - match crate::proxy::http::post::validation_middleware( + match crate::proxy::http::validation_middleware( &cfg, &signed_message, &proxy_route, From ce19db9f4309d2e5817a40d24217bb7542eeafa8 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 08:48:43 +0300 Subject: [PATCH 10/23] migrate http tests Signed-off-by: onur-ozkan --- src/net/http.rs | 59 ----------------------------------- src/proxy/http/mod.rs | 71 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 60 deletions(-) diff --git a/src/net/http.rs b/src/net/http.rs index d427a5d..432e99b 100644 --- a/src/net/http.rs +++ b/src/net/http.rs @@ -198,62 +198,3 @@ pub(crate) async fn http_handler( .await } -#[test] -fn test_get_proxy_route_by_inbound() { - use hyper::Uri; - use std::str::FromStr; - - let cfg = ctx::get_app_config_test_instance(); - - let proxy_route = cfg.get_proxy_route_by_inbound("/test").unwrap(); - - assert_eq!(proxy_route.outbound_route, "https://komodoplatform.com"); - - let proxy_route = cfg.get_proxy_route_by_inbound("/test-2").unwrap(); - - assert_eq!(proxy_route.outbound_route, "https://atomicdex.io"); - - let url = Uri::from_str("https://komodo.proxy:5535/nft-test").unwrap(); - let path = url.path().to_string(); - let proxy_route = cfg.get_proxy_route_by_inbound(&path).unwrap(); - assert_eq!(proxy_route.outbound_route, "https://nft.proxy"); -} - -#[test] -fn test_get_proxy_route_by_uri_inbound() { - use hyper::Uri; - use std::str::FromStr; - - let cfg = ctx::get_app_config_test_instance(); - - // test "/nft-test" inbound case - let mut url = Uri::from_str("https://komodo.proxy:5535/nft-test/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC").unwrap(); - let proxy_route = cfg.get_proxy_route_by_uri(&mut url).unwrap(); - assert_eq!(proxy_route.outbound_route, "https://nft.proxy"); - - // test "/nft-test/special" inbound case - let mut url = Uri::from_str("https://komodo.proxy:3333/nft-test/special/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC").unwrap(); - let proxy_route = cfg.get_proxy_route_by_uri(&mut url).unwrap(); - assert_eq!(proxy_route.outbound_route, "https://nft.special"); - - // test "/" inbound case - let mut url = Uri::from_str("https://komodo.proxy:0333/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC").unwrap(); - let proxy_route = cfg.get_proxy_route_by_uri(&mut url).unwrap(); - assert_eq!(proxy_route.outbound_route, "https://adex.io"); -} - -#[test] -fn test_respond_by_status() { - let all_supported_status_codes = [ - 100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, - 305, 307, 308, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 416, 417, 418, 421, 422, 423, 424, 426, 428, 429, 431, 451, 500, 501, 502, 503, 504, - 505, 506, 507, 508, 510, 511, - ]; - - for status_code in all_supported_status_codes { - let status_type = StatusCode::from_u16(status_code).unwrap(); - let res = response_by_status(status_type).unwrap(); - assert_eq!(res.status(), status_type); - } -} diff --git a/src/proxy/http/mod.rs b/src/proxy/http/mod.rs index 5ae06de..7c8fcb1 100644 --- a/src/proxy/http/mod.rs +++ b/src/proxy/http/mod.rs @@ -6,7 +6,9 @@ use proxy_signature::ProxySign; use crate::{ address_status::{AddressStatus, AddressStatusOperations}, ctx::{AppConfig, ProxyRoute}, - db::Db, log_format, rate_limiter::RateLimitOperations, + db::Db, + log_format, + rate_limiter::RateLimitOperations, }; pub(crate) mod get; @@ -96,3 +98,70 @@ pub(crate) async fn validation_middleware( } } } + +#[cfg(test)] +mod tests { + use hyper::StatusCode; + + use crate::{ctx, http::response_by_status}; + + #[test] + fn test_get_proxy_route_by_inbound() { + use hyper::Uri; + use std::str::FromStr; + + let cfg = ctx::get_app_config_test_instance(); + + let proxy_route = cfg.get_proxy_route_by_inbound("/test").unwrap(); + + assert_eq!(proxy_route.outbound_route, "https://komodoplatform.com"); + + let proxy_route = cfg.get_proxy_route_by_inbound("/test-2").unwrap(); + + assert_eq!(proxy_route.outbound_route, "https://atomicdex.io"); + + let url = Uri::from_str("https://komodo.proxy:5535/nft-test").unwrap(); + let path = url.path().to_string(); + let proxy_route = cfg.get_proxy_route_by_inbound(&path).unwrap(); + assert_eq!(proxy_route.outbound_route, "https://nft.proxy"); + } + + #[test] + fn test_get_proxy_route_by_uri_inbound() { + use hyper::Uri; + use std::str::FromStr; + + let cfg = ctx::get_app_config_test_instance(); + + // test "/nft-test" inbound case + let mut url = Uri::from_str("https://komodo.proxy:5535/nft-test/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC").unwrap(); + let proxy_route = cfg.get_proxy_route_by_uri(&mut url).unwrap(); + assert_eq!(proxy_route.outbound_route, "https://nft.proxy"); + + // test "/nft-test/special" inbound case + let mut url = Uri::from_str("https://komodo.proxy:3333/nft-test/special/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC").unwrap(); + let proxy_route = cfg.get_proxy_route_by_uri(&mut url).unwrap(); + assert_eq!(proxy_route.outbound_route, "https://nft.special"); + + // test "/" inbound case + let mut url = Uri::from_str("https://komodo.proxy:0333/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC").unwrap(); + let proxy_route = cfg.get_proxy_route_by_uri(&mut url).unwrap(); + assert_eq!(proxy_route.outbound_route, "https://adex.io"); + } + + #[test] + fn test_respond_by_status() { + let all_supported_status_codes = [ + 100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, + 304, 305, 307, 308, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, + 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 426, 428, 429, 431, 451, 500, 501, + 502, 503, 504, 505, 506, 507, 508, 510, 511, + ]; + + for status_code in all_supported_status_codes { + let status_type = StatusCode::from_u16(status_code).unwrap(); + let res = response_by_status(status_type).unwrap(); + assert_eq!(res.status(), status_type); + } + } +} From 5c69997da4ca604ea75df19803d789bd91847fbc Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 11:41:07 +0300 Subject: [PATCH 11/23] make the logging interface more convenient Signed-off-by: onur-ozkan --- .TODO | 1 - src/logger.rs | 27 ++++++ src/main.rs | 1 + src/net/http.rs | 88 +++++++++----------- src/net/server.rs | 24 ++---- src/proxy/http/get.rs | 51 +++++------- src/proxy/http/mod.rs | 66 +++++++-------- src/proxy/http/post.rs | 65 +++++++-------- src/proxy/websocket.rs | 185 ++++++++++++++++++----------------------- 9 files changed, 234 insertions(+), 274 deletions(-) create mode 100644 src/logger.rs diff --git a/.TODO b/.TODO index 686d42e..45d90a7 100644 --- a/.TODO +++ b/.TODO @@ -1,3 +1,2 @@ -- Simpler logging mechanism - Test coverage - Peer verification through KDF RPCs diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..3b85101 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,27 @@ +use std::fmt::Display; + +use log::Level; + +macro_rules! log_format { + ($ip: expr, $address: expr, $path: expr, $format: expr, $($args: tt)+) => {format!(concat!("[Ip: {} | Peer: {} | Endpoint: {}] ", $format), $ip, $address, $path, $($args)+)}; + ($ip: expr, $address: expr, $path: expr, $format: expr) => {format!(concat!("[Ip: {} | Peer: {} | Endpoint: {}] ", $format), $ip, $address, $path)} +} + +pub(crate) fn tracked_log( + log_level: Level, + ip: Ip, + peer_address: PeerAddress, + endpoint: Endpoint, + message: Message, +) where + Ip: Display, + PeerAddress: Display, + Endpoint: Display, + Message: Display, +{ + log::log!( + log_level, + "{}", + log_format!(ip, peer_address, endpoint, "{}", message) + ); +} diff --git a/src/main.rs b/src/main.rs index 372dde5..84a3f6e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use server::serve; mod address_status; mod ctx; mod db; +mod logger; #[path = "net/http.rs"] mod http; #[path = "security/jwt.rs"] diff --git a/src/net/http.rs b/src/net/http.rs index 432e99b..41c3ab2 100644 --- a/src/net/http.rs +++ b/src/net/http.rs @@ -1,4 +1,5 @@ use super::*; +use crate::logger::tracked_log; use crate::proxy::{generate_payload_from_req, proxy, validation_middleware}; use crate::server::is_private_ip; use address_status::{get_address_status_list, post_address_status}; @@ -71,14 +72,12 @@ pub(crate) async fn http_handler( let is_private_ip = is_private_ip(&remote_addr.ip()); if is_private_ip { - log::info!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "Request received from the same network. Security middlewares will be by-passed." - ) + tracked_log( + log::Level::Info, + remote_addr.ip(), + "**not-available**", + req.uri(), + "Request received from the same network. Security middlewares will be by-passed.", ); match (req.method(), req_uri.path()) { @@ -97,29 +96,26 @@ pub(crate) async fn http_handler( &Method::GET => match cfg.get_proxy_route_by_uri(req.uri_mut()) { Some(proxy_route) => proxy_route, None => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req_uri, - "Proxy route not found for GET request, returning 404." - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + "**not-available**", + req_uri, + "Proxy route not found for GET request, returning 404.", ); + return response_by_status(StatusCode::NOT_FOUND); } }, _ => match cfg.get_proxy_route_by_inbound(req.uri().path()) { Some(proxy_route) => proxy_route, None => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req_uri, - "Proxy route not found for non-GET request, returning 404." - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + "**not-available**", + req_uri, + "Proxy route not found for non-GET request, returning 404.", ); return response_by_status(StatusCode::NOT_FOUND); } @@ -129,41 +125,34 @@ pub(crate) async fn http_handler( let (req, payload) = match generate_payload_from_req(req, &proxy_route.proxy_type).await { Ok(t) => t, Err(e) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req_uri, - "Received invalid http payload: {}, returning 401.", - e - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + "**not-available**", + req_uri, + format!("Received invalid http payload: {e}, returning 401."), ); return response_by_status(StatusCode::UNAUTHORIZED); } }; - log::info!( - "{}", - log_format!( - remote_addr.ip(), - payload.proxy_sign().address, - req_uri, - "Request and payload data received." - ) + tracked_log( + log::Level::Info, + remote_addr.ip(), + &payload.proxy_sign().address, + &req_uri, + "Request and payload data received.", ); let x_forwarded_for: HeaderValue = match remote_addr.ip().to_string().parse() { Ok(t) => t, Err(_) => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - payload.proxy_sign().address, - req_uri, - "Error type casting of IpAddr into HeaderValue, returning 500." - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + &payload.proxy_sign().address, + &req_uri, + "Error type casting of IpAddr into HeaderValue, returning 500.", ); return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); } @@ -197,4 +186,3 @@ pub(crate) async fn http_handler( ) .await } - diff --git a/src/net/server.rs b/src/net/server.rs index aa4603c..8fa4919 100644 --- a/src/net/server.rs +++ b/src/net/server.rs @@ -8,16 +8,9 @@ use hyper::{Body, Request, Response, Server, StatusCode}; use super::{GenericError, GenericResult}; use crate::ctx::{AppConfig, DEFAULT_PORT}; use crate::http::{http_handler, response_by_status, X_FORWARDED_FOR}; -use crate::log_format; +use crate::logger::tracked_log; use crate::proxy::websocket::{should_upgrade_to_socket_conn, socket_handler}; -// TODO: replace this macro with a helper function. -#[macro_export] -macro_rules! log_format { - ($ip: expr, $address: expr, $path: expr, $format: expr, $($args: tt)+) => {format!(concat!("[Ip: {} | Address: {} | Path: {}] ", $format), $ip, $address, $path, $($args)+)}; - ($ip: expr, $address: expr, $path: expr, $format: expr) => {format!(concat!("[Ip: {} | Address: {} | Path: {}] ", $format), $ip, $address, $path)} -} - pub(crate) fn is_private_ip(ip: &IpAddr) -> bool { match ip { IpAddr::V4(v4) => v4.is_private() || v4.is_loopback(), @@ -50,15 +43,14 @@ async fn connection_handler( let remote_addr = match get_real_address(&req, &remote_addr) { Ok(t) => t, _ => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "Reading real remote address failed, returning 500." - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + "Reading real remote address failed, returning 500.", ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); } }; diff --git a/src/proxy/http/get.rs b/src/proxy/http/get.rs index 5a5272f..a514498 100644 --- a/src/proxy/http/get.rs +++ b/src/proxy/http/get.rs @@ -1,12 +1,10 @@ -use crate::address_status::{AddressStatus, AddressStatusOperations}; use crate::ctx::{AppConfig, ProxyRoute}; -use crate::db::Db; use crate::http::{ insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, }; +use crate::logger::tracked_log; use crate::proxy::remove_hop_by_hop_headers; -use crate::rate_limiter::RateLimitOperations; -use crate::{log_format, GenericResult}; +use crate::GenericResult; use hyper::header::{HeaderName, HeaderValue}; use hyper::{header, Body, Request, Response, StatusCode, Uri}; use hyper_tls::HttpsConnector; @@ -23,15 +21,15 @@ pub(crate) async fn proxy( ) -> GenericResult> { if proxy_route.authorized { if let Err(e) = insert_jwt_to_http_header(cfg, req.headers_mut()).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req.uri(), + tracked_log( + log::Level::Error, + remote_addr.ip(), + signed_message.address, + req.uri(), + format!( "Error inserting JWT into HTTP header: {}, returning 500.", e - ) + ), ); return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); } @@ -40,15 +38,12 @@ pub(crate) async fn proxy( let original_req_uri = req.uri().clone(); if let Err(e) = modify_request_uri(&mut req, proxy_route) { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - original_req_uri, - "Error modifying request base Uri: {}, returning 500.", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + signed_message.address, + original_req_uri, + format!("Error modifying request base Uri: {}, returning 500.", e), ); return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); } @@ -67,16 +62,12 @@ pub(crate) async fn proxy( let res = match client.request(req).await { Ok(t) => t, Err(e) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - original_req_uri, - "Couldn't reach {}: {}. Returning 503.", - target_uri, - e - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + signed_message.address, + original_req_uri, + format!("Couldn't reach {}: {}. Returning 503.", target_uri, e), ); return response_by_status(StatusCode::SERVICE_UNAVAILABLE); } diff --git a/src/proxy/http/mod.rs b/src/proxy/http/mod.rs index 7c8fcb1..3ea69fe 100644 --- a/src/proxy/http/mod.rs +++ b/src/proxy/http/mod.rs @@ -7,13 +7,14 @@ use crate::{ address_status::{AddressStatus, AddressStatusOperations}, ctx::{AppConfig, ProxyRoute}, db::Db, - log_format, + logger::tracked_log, rate_limiter::RateLimitOperations, }; pub(crate) mod get; pub(crate) mod post; +// TODO: Query peers on KDF seeds pub(crate) async fn validation_middleware( cfg: &AppConfig, signed_message: &ProxySign, @@ -28,14 +29,12 @@ pub(crate) async fn validation_middleware( AddressStatus::Blocked => Err(StatusCode::FORBIDDEN), AddressStatus::None => { if !signed_message.is_valid_message() { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Request has invalid signed message, returning 401" - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + &signed_message.address, + req_uri, + "Request has invalid signed message, returning 401", ); return Err(StatusCode::UNAUTHORIZED); @@ -51,45 +50,40 @@ pub(crate) async fn validation_middleware( match db.rate_exceeded(&rate_limiter_key, rate_limiter).await { Ok(false) => {} Ok(true) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, - "Rate exceed for {}, returning 406.", - rate_limiter_key, - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + &signed_message.address, + req_uri, + format!("Rate exceed for {}, returning 406.", rate_limiter_key), ); return Err(StatusCode::NOT_ACCEPTABLE); } Err(e) => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, + tracked_log( + log::Level::Error, + remote_addr.ip(), + &signed_message.address, + req_uri, + format!( "Rate exceeded check failed for node '{}': {}, returning 500.", - signed_message.address, - e - ) + signed_message.address, e + ), ); return Err(StatusCode::INTERNAL_SERVER_ERROR); } } if let Err(e) = db.rate_address(rate_limiter_key).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - signed_message.address, - req_uri, + tracked_log( + log::Level::Error, + remote_addr.ip(), + &signed_message.address, + req_uri, + format!( "Rate incrementing failed for node '{}': {}, returning 500.", - signed_message.address, - e - ) + signed_message.address, e + ), ); return Err(StatusCode::INTERNAL_SERVER_ERROR); }; diff --git a/src/proxy/http/post.rs b/src/proxy/http/post.rs index 56f91ec..c159da5 100644 --- a/src/proxy/http/post.rs +++ b/src/proxy/http/post.rs @@ -2,9 +2,10 @@ use crate::ctx::{AppConfig, ProxyRoute}; use crate::http::{ insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, }; +use crate::logger::tracked_log; use crate::proxy::remove_hop_by_hop_headers; use crate::rpc::RpcPayload; -use crate::{log_format, GenericResult}; +use crate::GenericResult; use hyper::header::{HeaderName, HeaderValue}; use hyper::{header, Body, Request, Response, StatusCode}; use hyper_tls::HttpsConnector; @@ -24,15 +25,12 @@ pub(crate) async fn proxy( if !proxy_route.allowed_rpc_methods.is_empty() && !proxy_route.allowed_rpc_methods.contains(&payload.method) { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req.uri(), - "Method {} not allowed for, returning 403.", - payload.method - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + proxy_sign.address, + req.uri(), + format!("Method {} not allowed for, returning 403.", payload.method), ); return response_by_status(StatusCode::FORBIDDEN); } @@ -43,14 +41,12 @@ pub(crate) async fn proxy( .await .is_err() { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - req.uri(), - "Error inserting JWT into http header, returning 500." - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + proxy_sign.address, + req.uri(), + "Error inserting JWT into http header, returning 500.", ); return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); } @@ -60,16 +56,15 @@ pub(crate) async fn proxy( *req.uri_mut() = match proxy_route.outbound_route.parse() { Ok(uri) => uri, Err(e) => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - original_req_uri, + tracked_log( + log::Level::Error, + remote_addr.ip(), + proxy_sign.address, + original_req_uri, + format!( "Error type casting value of {} into Uri: {}, returning 500.", - proxy_route.outbound_route, - e - ) + proxy_route.outbound_route, e + ), ); return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); } @@ -89,16 +84,12 @@ pub(crate) async fn proxy( let res = match client.request(req).await { Ok(t) => t, Err(e) => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - proxy_sign.address, - original_req_uri, - "Couldn't reach {}: {}. Returning 503.", - target_uri, - e - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + proxy_sign.address, + original_req_uri, + format!("Couldn't reach {}: {}. Returning 503.", target_uri, e), ); return response_by_status(StatusCode::SERVICE_UNAVAILABLE); } diff --git a/src/proxy/websocket.rs b/src/proxy/websocket.rs index 647fd2f..303d8f1 100644 --- a/src/proxy/websocket.rs +++ b/src/proxy/websocket.rs @@ -9,7 +9,8 @@ use tokio_tungstenite::{ }; use crate::{ - ctx::AppConfig, http::response_by_status, log_format, rpc::RpcSocketPayload, GenericResult, + ctx::AppConfig, http::response_by_status, logger::tracked_log, rpc::RpcSocketPayload, + GenericResult, }; pub(crate) fn should_upgrade_to_socket_conn(req: &Request) -> bool { @@ -26,14 +27,12 @@ pub(crate) async fn socket_handler( let proxy_route = match cfg.get_proxy_route_by_inbound(&inbound_route) { Some(proxy_route) => proxy_route.clone(), None => { - log::warn!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "Proxy route not found for socket, returning 404." - ) + tracked_log( + log::Level::Warn, + remote_addr.ip(), + "**not-available**", + req.uri(), + "Proxy route not found for socket, returning 404.", ); return response_by_status(StatusCode::NOT_FOUND); } @@ -47,14 +46,12 @@ pub(crate) async fn socket_handler( .await .is_err() { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "Proxy route not found for socket" - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + "Proxy route not found for socket", ); return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); @@ -83,28 +80,23 @@ pub(crate) async fn socket_handler( futures_util::select! { _ = keepalive_interval.tick().fuse() => { if let Err(e) = outbound_socket.send(Message::Ping(Vec::new())).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); + }; if let Err(e) = inbound_socket.send(Message::Ping(Vec::new())).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); } } @@ -113,15 +105,12 @@ pub(crate) async fn socket_handler( match msg { Some(Ok(msg)) => { if let Err(e) = inbound_socket.send(msg).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); }; }, @@ -137,15 +126,12 @@ pub(crate) async fn socket_handler( Ok(t) => t, Err(e) => { if let Err(e) = inbound_socket.send(format!("Invalid payload. {e}").into()).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); }; continue; @@ -155,15 +141,12 @@ pub(crate) async fn socket_handler( if !proxy_route.allowed_rpc_methods.contains(&payload.method) { if let Err(e) = inbound_socket.send("Method not allowed.".into()).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); }; continue; @@ -188,44 +171,35 @@ pub(crate) async fn socket_handler( .to_string(); if let Err(e) = outbound_socket.send(msg.into()).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); }; }, Err(status_code) => { if let Err(e) = inbound_socket.send(format!("{status_code}").into()).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); }; } } } else if let Err(e) = outbound_socket.send(msg).await { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e ); } }, @@ -236,23 +210,23 @@ pub(crate) async fn socket_handler( } } e => { - log::error!( - "{}", - log_format!( - remote_addr.ip(), - String::from("-"), - req.uri(), - "{:?}", - e - ) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + format!("{e:?}"), ); } }; } Err(e) => { - log::error!( - "{}", - log_format!(remote_addr.ip(), String::from("-"), req.uri(), "{}", e) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e, ); } } @@ -261,9 +235,12 @@ pub(crate) async fn socket_handler( Ok(response) } Err(e) => { - log::error!( - "{}", - log_format!(remote_addr.ip(), String::from("-"), req.uri(), "{}", e) + tracked_log( + log::Level::Error, + remote_addr.ip(), + "**not-available**", + req.uri(), + e, ); response_by_status(StatusCode::SERVICE_UNAVAILABLE) } From f67e569de6bb651304ccd71805a16e38f1dd67a0 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 13:00:02 +0300 Subject: [PATCH 12/23] migrate http module Signed-off-by: onur-ozkan --- src/main.rs | 8 +- src/net/http.rs | 188 --------------------------------- src/net/rpc.rs | 3 +- src/net/server.rs | 2 +- src/proxy/http/get.rs | 13 ++- src/proxy/http/mod.rs | 2 +- src/proxy/http/post.rs | 8 +- src/proxy/mod.rs | 181 ++++++++++++++++++++++++++++++- src/proxy/websocket.rs | 9 +- src/security/address_status.rs | 3 +- 10 files changed, 203 insertions(+), 214 deletions(-) delete mode 100644 src/net/http.rs diff --git a/src/main.rs b/src/main.rs index 84a3f6e..6902fc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,11 +6,9 @@ use server::serve; mod address_status; mod ctx; mod db; -mod logger; -#[path = "net/http.rs"] -mod http; #[path = "security/jwt.rs"] mod jwt; +mod logger; mod proxy; #[path = "security/rate_limiter.rs"] mod rate_limiter; @@ -26,10 +24,10 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; /// A type alias for a generic error, encompassing any error that implements the `Error` trait, /// along with traits for thread-safe error handling (`Send` and `Sync`). /// This type is typically used across the application to handle errors uniformly. -type GenericError = Box; +pub(crate) type GenericError = Box; /// A type alias for a generic result, used throughout the application to encapsulate the /// outcome of operations that might fail with a `GenericError`. -type GenericResult = std::result::Result; +pub(crate) type GenericResult = std::result::Result; #[tokio::main] async fn main() -> GenericResult<()> { diff --git a/src/net/http.rs b/src/net/http.rs deleted file mode 100644 index 41c3ab2..0000000 --- a/src/net/http.rs +++ /dev/null @@ -1,188 +0,0 @@ -use super::*; -use crate::logger::tracked_log; -use crate::proxy::{generate_payload_from_req, proxy, validation_middleware}; -use crate::server::is_private_ip; -use address_status::{get_address_status_list, post_address_status}; -use ctx::AppConfig; -use hyper::{ - header::{self, HeaderValue}, - Body, HeaderMap, Method, Request, Response, StatusCode, -}; -use jwt::{get_cached_token_or_generate_one, JwtClaims}; -use serde_json::json; -use std::net::SocketAddr; - -/// Header value for `hyper::header::CONTENT_TYPE` -pub(crate) const APPLICATION_JSON: &str = "application/json"; -/// Represents `X-Forwarded-For` Header key -pub(crate) const X_FORWARDED_FOR: &str = "x-forwarded-for"; -async fn get_healthcheck() -> GenericResult> { - let json = json!({ - "health": "ok", - }); - - Ok(Response::builder() - .status(StatusCode::OK) - .header("Access-Control-Allow-Origin", "*") - .header("Access-Control-Allow-Headers", "*") - .header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") - .header(header::CONTENT_TYPE, APPLICATION_JSON) - .body(Body::from(json.to_string()))?) -} - -fn handle_preflight() -> GenericResult> { - Ok(Response::builder() - .status(StatusCode::NO_CONTENT) - .header("Access-Control-Allow-Origin", "*") - .header("Access-Control-Allow-Headers", "*") - .header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") - .body(Body::from(Vec::new()))?) -} - -pub(crate) fn response_by_status(status: StatusCode) -> GenericResult> { - Ok(Response::builder() - .status(status) - .header("Access-Control-Allow-Origin", "*") - .header("Access-Control-Allow-Headers", "*") - .header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") - .body(Body::from(Vec::new()))?) -} - -pub(crate) async fn insert_jwt_to_http_header( - cfg: &AppConfig, - headers: &mut HeaderMap, -) -> GenericResult<()> { - let claims = &JwtClaims::new(cfg.token_expiration_time()); - let auth_token = get_cached_token_or_generate_one(cfg, claims).await?; - headers.insert( - header::AUTHORIZATION, - format!("Bearer {}", auth_token).parse()?, - ); - - Ok(()) -} - -pub(crate) async fn http_handler( - cfg: &AppConfig, - mut req: Request, - remote_addr: SocketAddr, -) -> GenericResult> { - let req_uri = req.uri().clone(); - - let is_private_ip = is_private_ip(&remote_addr.ip()); - - if is_private_ip { - tracked_log( - log::Level::Info, - remote_addr.ip(), - "**not-available**", - req.uri(), - "Request received from the same network. Security middlewares will be by-passed.", - ); - - match (req.method(), req_uri.path()) { - (&Method::GET, "/") => return get_healthcheck().await, - (&Method::GET, "/address-status") => return get_address_status_list(cfg).await, - (&Method::POST, "/address-status") => return post_address_status(cfg, req).await, - _ => {} - }; - }; - - if req.method() == Method::OPTIONS { - return handle_preflight(); - } - - let proxy_route = match req.method() { - &Method::GET => match cfg.get_proxy_route_by_uri(req.uri_mut()) { - Some(proxy_route) => proxy_route, - None => { - tracked_log( - log::Level::Warn, - remote_addr.ip(), - "**not-available**", - req_uri, - "Proxy route not found for GET request, returning 404.", - ); - - return response_by_status(StatusCode::NOT_FOUND); - } - }, - _ => match cfg.get_proxy_route_by_inbound(req.uri().path()) { - Some(proxy_route) => proxy_route, - None => { - tracked_log( - log::Level::Warn, - remote_addr.ip(), - "**not-available**", - req_uri, - "Proxy route not found for non-GET request, returning 404.", - ); - return response_by_status(StatusCode::NOT_FOUND); - } - }, - }; - - let (req, payload) = match generate_payload_from_req(req, &proxy_route.proxy_type).await { - Ok(t) => t, - Err(e) => { - tracked_log( - log::Level::Warn, - remote_addr.ip(), - "**not-available**", - req_uri, - format!("Received invalid http payload: {e}, returning 401."), - ); - return response_by_status(StatusCode::UNAUTHORIZED); - } - }; - - tracked_log( - log::Level::Info, - remote_addr.ip(), - &payload.proxy_sign().address, - &req_uri, - "Request and payload data received.", - ); - - let x_forwarded_for: HeaderValue = match remote_addr.ip().to_string().parse() { - Ok(t) => t, - Err(_) => { - tracked_log( - log::Level::Error, - remote_addr.ip(), - &payload.proxy_sign().address, - &req_uri, - "Error type casting of IpAddr into HeaderValue, returning 500.", - ); - return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); - } - }; - - if is_private_ip { - return proxy( - cfg, - req, - &remote_addr, - payload, - x_forwarded_for, - proxy_route, - ) - .await; - } - - if let Err(status_code) = - validation_middleware(cfg, &payload, proxy_route, req.uri(), &remote_addr).await - { - return response_by_status(status_code); - } - - proxy( - cfg, - req, - &remote_addr, - payload, - x_forwarded_for, - proxy_route, - ) - .await -} diff --git a/src/net/rpc.rs b/src/net/rpc.rs index f570c61..250e61b 100644 --- a/src/net/rpc.rs +++ b/src/net/rpc.rs @@ -2,13 +2,14 @@ use bytes::Buf; use ctx::AppConfig; -use http::{insert_jwt_to_http_header, APPLICATION_JSON}; use hyper::{body::aggregate, header, Body, Request}; use hyper_tls::HttpsConnector; use proxy_signature::ProxySign; use serde::{Deserialize, Serialize}; use serde_json::from_reader; +use crate::proxy::{insert_jwt_to_http_header, APPLICATION_JSON}; + use super::*; pub(crate) type Json = serde_json::Value; diff --git a/src/net/server.rs b/src/net/server.rs index 8fa4919..17bad6c 100644 --- a/src/net/server.rs +++ b/src/net/server.rs @@ -7,9 +7,9 @@ use hyper::{Body, Request, Response, Server, StatusCode}; use super::{GenericError, GenericResult}; use crate::ctx::{AppConfig, DEFAULT_PORT}; -use crate::http::{http_handler, response_by_status, X_FORWARDED_FOR}; use crate::logger::tracked_log; use crate::proxy::websocket::{should_upgrade_to_socket_conn, socket_handler}; +use crate::proxy::{http_handler, response_by_status, X_FORWARDED_FOR}; pub(crate) fn is_private_ip(ip: &IpAddr) -> bool { match ip { diff --git a/src/proxy/http/get.rs b/src/proxy/http/get.rs index a514498..d0a4ba9 100644 --- a/src/proxy/http/get.rs +++ b/src/proxy/http/get.rs @@ -1,9 +1,9 @@ use crate::ctx::{AppConfig, ProxyRoute}; -use crate::http::{ - insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, -}; use crate::logger::tracked_log; -use crate::proxy::remove_hop_by_hop_headers; +use crate::proxy::{ + insert_jwt_to_http_header, remove_hop_by_hop_headers, response_by_status, APPLICATION_JSON, + X_FORWARDED_FOR, +}; use crate::GenericResult; use hyper::header::{HeaderName, HeaderValue}; use hyper::{header, Body, Request, Response, StatusCode, Uri}; @@ -105,12 +105,11 @@ fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> Gene #[cfg(test)] mod tests { use super::*; + use hyper::header::HeaderName; + use hyper::Method; #[tokio::test] async fn test_parse_payload() { - // use hyper::header::HeaderName; - use hyper::Method; - let serialized_payload = serde_json::json!({ "coin_ticker": "BTC", "address": "dummy-value", diff --git a/src/proxy/http/mod.rs b/src/proxy/http/mod.rs index 3ea69fe..0b4ff1a 100644 --- a/src/proxy/http/mod.rs +++ b/src/proxy/http/mod.rs @@ -97,7 +97,7 @@ pub(crate) async fn validation_middleware( mod tests { use hyper::StatusCode; - use crate::{ctx, http::response_by_status}; + use crate::{ctx, proxy::response_by_status}; #[test] fn test_get_proxy_route_by_inbound() { diff --git a/src/proxy/http/post.rs b/src/proxy/http/post.rs index c159da5..b684d48 100644 --- a/src/proxy/http/post.rs +++ b/src/proxy/http/post.rs @@ -1,9 +1,9 @@ use crate::ctx::{AppConfig, ProxyRoute}; -use crate::http::{ - insert_jwt_to_http_header, response_by_status, APPLICATION_JSON, X_FORWARDED_FOR, -}; use crate::logger::tracked_log; -use crate::proxy::remove_hop_by_hop_headers; +use crate::proxy::{ + insert_jwt_to_http_header, remove_hop_by_hop_headers, response_by_status, APPLICATION_JSON, + X_FORWARDED_FOR, +}; use crate::rpc::RpcPayload; use crate::GenericResult; use hyper::header::{HeaderName, HeaderValue}; diff --git a/src/proxy/mod.rs b/src/proxy/mod.rs index 13b569c..76e4ed9 100644 --- a/src/proxy/mod.rs +++ b/src/proxy/mod.rs @@ -1,16 +1,24 @@ +use crate::address_status::{get_address_status_list, post_address_status}; use crate::ctx::{AppConfig, GenericResult, ProxyRoute}; +use crate::jwt::{get_cached_token_or_generate_one, JwtClaims}; +use crate::logger::tracked_log; use crate::rpc::RpcPayload; -use hyper::header; +use crate::server::is_private_ip; use hyper::header::{HeaderName, HeaderValue}; +use hyper::{header, HeaderMap, Method}; use hyper::{Body, Request, Response, StatusCode, Uri}; use proxy_signature::ProxySign; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; +use serde_json::json; use std::net::SocketAddr; pub(crate) mod http; pub(crate) mod websocket; +pub(crate) const APPLICATION_JSON: &str = "application/json"; +pub(crate) const X_FORWARDED_FOR: &str = "x-forwarded-for"; + const X_AUTH_PAYLOAD: &str = "X-Auth-Payload"; const KEEP_ALIVE: &str = "keep-alive"; @@ -224,3 +232,174 @@ fn remove_hop_by_hop_headers( Ok(()) } + +async fn get_healthcheck() -> GenericResult> { + let json = json!({ + "health": "ok", + }); + + Ok(Response::builder() + .status(StatusCode::OK) + .header("Access-Control-Allow-Origin", "*") + .header("Access-Control-Allow-Headers", "*") + .header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") + .header(header::CONTENT_TYPE, APPLICATION_JSON) + .body(Body::from(json.to_string()))?) +} + +fn handle_preflight() -> GenericResult> { + Ok(Response::builder() + .status(StatusCode::NO_CONTENT) + .header("Access-Control-Allow-Origin", "*") + .header("Access-Control-Allow-Headers", "*") + .header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") + .body(Body::from(Vec::new()))?) +} + +pub(crate) fn response_by_status(status: StatusCode) -> GenericResult> { + Ok(Response::builder() + .status(status) + .header("Access-Control-Allow-Origin", "*") + .header("Access-Control-Allow-Headers", "*") + .header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") + .body(Body::from(Vec::new()))?) +} + +pub(crate) async fn insert_jwt_to_http_header( + cfg: &AppConfig, + headers: &mut HeaderMap, +) -> GenericResult<()> { + let claims = &JwtClaims::new(cfg.token_expiration_time()); + let auth_token = get_cached_token_or_generate_one(cfg, claims).await?; + headers.insert( + header::AUTHORIZATION, + format!("Bearer {}", auth_token).parse()?, + ); + + Ok(()) +} + +pub(crate) async fn http_handler( + cfg: &AppConfig, + mut req: Request, + remote_addr: SocketAddr, +) -> GenericResult> { + let req_uri = req.uri().clone(); + + let is_private_ip = is_private_ip(&remote_addr.ip()); + + if is_private_ip { + tracked_log( + log::Level::Info, + remote_addr.ip(), + "**not-available**", + req.uri(), + "Request received from the same network. Security middlewares will be by-passed.", + ); + + match (req.method(), req_uri.path()) { + (&Method::GET, "/") => return get_healthcheck().await, + (&Method::GET, "/address-status") => return get_address_status_list(cfg).await, + (&Method::POST, "/address-status") => return post_address_status(cfg, req).await, + _ => {} + }; + }; + + if req.method() == Method::OPTIONS { + return handle_preflight(); + } + + let proxy_route = match req.method() { + &Method::GET => match cfg.get_proxy_route_by_uri(req.uri_mut()) { + Some(proxy_route) => proxy_route, + None => { + tracked_log( + log::Level::Warn, + remote_addr.ip(), + "**not-available**", + req_uri, + "Proxy route not found for GET request, returning 404.", + ); + + return response_by_status(StatusCode::NOT_FOUND); + } + }, + _ => match cfg.get_proxy_route_by_inbound(req.uri().path()) { + Some(proxy_route) => proxy_route, + None => { + tracked_log( + log::Level::Warn, + remote_addr.ip(), + "**not-available**", + req_uri, + "Proxy route not found for non-GET request, returning 404.", + ); + return response_by_status(StatusCode::NOT_FOUND); + } + }, + }; + + let (req, payload) = match generate_payload_from_req(req, &proxy_route.proxy_type).await { + Ok(t) => t, + Err(e) => { + tracked_log( + log::Level::Warn, + remote_addr.ip(), + "**not-available**", + req_uri, + format!("Received invalid http payload: {e}, returning 401."), + ); + return response_by_status(StatusCode::UNAUTHORIZED); + } + }; + + tracked_log( + log::Level::Info, + remote_addr.ip(), + &payload.proxy_sign().address, + &req_uri, + "Request and payload data received.", + ); + + let x_forwarded_for: HeaderValue = match remote_addr.ip().to_string().parse() { + Ok(t) => t, + Err(_) => { + tracked_log( + log::Level::Error, + remote_addr.ip(), + &payload.proxy_sign().address, + &req_uri, + "Error type casting of IpAddr into HeaderValue, returning 500.", + ); + return response_by_status(StatusCode::INTERNAL_SERVER_ERROR); + } + }; + + if is_private_ip { + return proxy( + cfg, + req, + &remote_addr, + payload, + x_forwarded_for, + proxy_route, + ) + .await; + } + + if let Err(status_code) = + validation_middleware(cfg, &payload, proxy_route, req.uri(), &remote_addr).await + { + return response_by_status(status_code); + } + + proxy( + cfg, + req, + &remote_addr, + payload, + x_forwarded_for, + proxy_route, + ) + .await +} diff --git a/src/proxy/websocket.rs b/src/proxy/websocket.rs index 303d8f1..0eefc87 100644 --- a/src/proxy/websocket.rs +++ b/src/proxy/websocket.rs @@ -8,10 +8,9 @@ use tokio_tungstenite::{ WebSocketStream, }; -use crate::{ - ctx::AppConfig, http::response_by_status, logger::tracked_log, rpc::RpcSocketPayload, - GenericResult, -}; +use crate::{ctx::AppConfig, logger::tracked_log, rpc::RpcSocketPayload, GenericResult}; + +use super::{insert_jwt_to_http_header, response_by_status}; pub(crate) fn should_upgrade_to_socket_conn(req: &Request) -> bool { let expected = HeaderValue::from_static("websocket"); @@ -42,7 +41,7 @@ pub(crate) async fn socket_handler( if proxy_route.authorized { // modify outgoing request - if crate::http::insert_jwt_to_http_header(&cfg, outbound_req.headers_mut()) + if insert_jwt_to_http_header(&cfg, outbound_req.headers_mut()) .await .is_err() { diff --git a/src/security/address_status.rs b/src/security/address_status.rs index b9dd8c7..d904b41 100644 --- a/src/security/address_status.rs +++ b/src/security/address_status.rs @@ -2,11 +2,12 @@ use async_trait::async_trait; use bytes::Buf; use ctx::AppConfig; use db::Db; -use http::APPLICATION_JSON; use hyper::{header, Body, Request, Response, StatusCode}; use redis::FromRedisValue; use serde::{Deserialize, Serialize}; +use crate::proxy::APPLICATION_JSON; + use super::*; pub(crate) const DB_STATUS_LIST: &str = "status_list"; From 77d53c9b65473ac5909d045e91538655bb363bb5 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 13:44:49 +0300 Subject: [PATCH 13/23] extend signing test Signed-off-by: onur-ozkan --- Cargo.lock | 1 + Cargo.toml | 1 + src/proxy/http/get.rs | 52 +++++++++++++++++++++---------------------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3d192a..2071262 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1062,6 +1062,7 @@ dependencies = [ "hyper-tls", "jemallocator", "jsonwebtoken", + "libp2p", "log", "once_cell", "proxy_signature", diff --git a/Cargo.toml b/Cargo.toml index 1421eaa..61158b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ tokio = { version = "1.12.0", default-features = false, features = ["macros", "r tokio-tungstenite = { version = "0.20.0", features = ["native-tls"] } # From our sources # proxy_signature = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", rev = "0784f1a" } +libp2p = { git = "https://github.com/KomodoPlatform/rust-libp2p.git", tag = "k-0.52.4", default-features = false, features = ["identify"] } proxy_signature = { path = "/home/nimda/devspace/KomodoPlatform/komodo-defi-framework/mm2src/proxy_signature" } [target.x86_64-unknown-linux-gnu.dependencies] diff --git a/src/proxy/http/get.rs b/src/proxy/http/get.rs index d0a4ba9..3f18483 100644 --- a/src/proxy/http/get.rs +++ b/src/proxy/http/get.rs @@ -104,31 +104,40 @@ fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> Gene #[cfg(test)] mod tests { + use crate::proxy::X_AUTH_PAYLOAD; + use super::*; use hyper::header::HeaderName; use hyper::Method; + use libp2p::identity; + use proxy_signature::RawMessage; + + fn generate_ed25519_keypair(mut p2p_key: [u8; 32]) -> identity::Keypair { + let secret = identity::ed25519::SecretKey::try_from_bytes(&mut p2p_key) + .expect("Secret length is 32 bytes"); + let keypair = identity::ed25519::Keypair::from(secret); + identity::Keypair::from(keypair) + } #[tokio::test] - async fn test_parse_payload() { - let serialized_payload = serde_json::json!({ - "coin_ticker": "BTC", - "address": "dummy-value", - "timestamp_message": 1655320000, - "signature": "dummy-value", - }) - .to_string(); + async fn sign_serialize_and_send() { + let keypair = generate_ed25519_keypair([0; 32]); + let proxy_sign = + RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + let serialized_proxy_sign = serde_json::to_string(&proxy_sign).unwrap(); let req = Request::builder() .method(Method::GET) .header(header::ACCEPT, HeaderValue::from_static(APPLICATION_JSON)) .header( crate::proxy::X_AUTH_PAYLOAD, - HeaderValue::from_str(&serialized_payload).unwrap(), + HeaderValue::from_str(&serialized_proxy_sign).unwrap(), ) .body(Body::empty()) .unwrap(); - let (mut req, payload) = crate::proxy::parse_auth_header(req).await.unwrap(); + let (mut req, deserialized_proxy_sign) = + crate::proxy::parse_auth_header(req).await.unwrap(); let body_bytes = hyper::body::to_bytes(req.body_mut()).await.unwrap(); assert!( @@ -136,23 +145,14 @@ mod tests { "Body should be empty for GET methods" ); - let header_value = req.headers().get(header::ACCEPT).unwrap(); - - // let expected_payload = SignedMessage { - // coin_ticker: String::from("BTC"), - // address: String::from("dummy-value"), - // timestamp_message: 1655320000, - // signature: String::from("dummy-value"), - // }; - - // assert_eq!(payload, expected_payload); - // assert_eq!(header_value, APPLICATION_JSON); + assert_eq!(deserialized_proxy_sign, proxy_sign); + assert!(deserialized_proxy_sign.is_valid_message()); - // let additional_headers = &[ - // header::CONTENT_LENGTH, - // HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), - // ]; - // remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); + let additional_headers = &[ + header::CONTENT_LENGTH, + HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), + ]; + remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); } #[tokio::test] From 89767307ce233eee34491c7e477ae73bb9f8a9c4 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:05:53 +0300 Subject: [PATCH 14/23] extend RPC tests Signed-off-by: onur-ozkan --- src/net/rpc.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/net/rpc.rs b/src/net/rpc.rs index 250e61b..6897d82 100644 --- a/src/net/rpc.rs +++ b/src/net/rpc.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] // We will need this module for KDF RPCs +#![allow(dead_code)] // TODO: remove this use bytes::Buf; use ctx::AppConfig; @@ -124,3 +124,44 @@ async fn test_send() { assert_eq!(res, expected_res); } + +#[test] +fn payload_serialzation_and_deserialization() { + let json_payload = serde_json::json!({ + "method": "dummy-value", + "params": [], + "id": 1, + "jsonrpc": "2.0" + }); + + let actual_payload: RpcPayload = serde_json::from_str(&json_payload.to_string()).unwrap(); + + let expected_payload = RpcPayload { + method: String::from("dummy-value"), + params: serde_json::json!([]), + id: Id::Number(1), + jsonrpc: String::from("2.0"), + }; + + assert_eq!(actual_payload, expected_payload); + + // Backwards + let json = serde_json::to_value(expected_payload).unwrap(); + assert_eq!(json_payload, json); + assert_eq!(json_payload.to_string(), json.to_string()); +} + +#[test] +fn string_id_serialzation_and_deserialization() { + let json_payload = serde_json::json!({ + "method": "dummy-value", + "params": [], + "id": "string-id-123", + "jsonrpc": "2.0" + }); + + let deserialized: RpcPayload = serde_json::from_str(&json_payload.to_string()).unwrap(); + let expected_id = Id::String("string-id-123".into()); + + assert_eq!(deserialized.id, expected_id); +} From a5de7bba0b3f7f414bea99cdacfd1902e26bfe6c Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:10:43 +0300 Subject: [PATCH 15/23] extend the test coverage Signed-off-by: onur-ozkan --- .TODO | 1 - src/proxy/http/get.rs | 5 +- src/proxy/http/mod.rs | 109 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 112 insertions(+), 3 deletions(-) diff --git a/.TODO b/.TODO index 45d90a7..ae4b42d 100644 --- a/.TODO +++ b/.TODO @@ -1,2 +1 @@ -- Test coverage - Peer verification through KDF RPCs diff --git a/src/proxy/http/get.rs b/src/proxy/http/get.rs index 3f18483..3cae11d 100644 --- a/src/proxy/http/get.rs +++ b/src/proxy/http/get.rs @@ -78,7 +78,10 @@ pub(crate) async fn proxy( /// This function removes the matched inbound route from the request URI and /// replaces request base URI with the outbound route specified in the proxy route. -fn modify_request_uri(req: &mut Request, proxy_route: &ProxyRoute) -> GenericResult<()> { +pub(crate) fn modify_request_uri( + req: &mut Request, + proxy_route: &ProxyRoute, +) -> GenericResult<()> { let proxy_base_uri = proxy_route.outbound_route.parse::()?; let original_uri = req.uri(); diff --git a/src/proxy/http/mod.rs b/src/proxy/http/mod.rs index 0b4ff1a..5c46c45 100644 --- a/src/proxy/http/mod.rs +++ b/src/proxy/http/mod.rs @@ -95,10 +95,25 @@ pub(crate) async fn validation_middleware( #[cfg(test)] mod tests { - use hyper::StatusCode; + use hyper::{header, Body, Request, StatusCode}; + use crate::proxy::http::get::modify_request_uri; + use crate::proxy::{remove_hop_by_hop_headers, HeaderValue, APPLICATION_JSON, X_AUTH_PAYLOAD}; use crate::{ctx, proxy::response_by_status}; + use super::*; + use hyper::header::HeaderName; + use hyper::Method; + use libp2p::identity; + use proxy_signature::RawMessage; + + fn generate_ed25519_keypair(mut p2p_key: [u8; 32]) -> identity::Keypair { + let secret = identity::ed25519::SecretKey::try_from_bytes(&mut p2p_key) + .expect("Secret length is 32 bytes"); + let keypair = identity::ed25519::Keypair::from(secret); + identity::Keypair::from(keypair) + } + #[test] fn test_get_proxy_route_by_inbound() { use hyper::Uri; @@ -158,4 +173,96 @@ mod tests { assert_eq!(res.status(), status_type); } } + + #[tokio::test] + async fn sign_serialize_and_send() { + let keypair = generate_ed25519_keypair([0; 32]); + let proxy_sign = + RawMessage::sign(&keypair, &Uri::from_static("http://example.com"), 0, 5).unwrap(); + let serialized_proxy_sign = serde_json::to_string(&proxy_sign).unwrap(); + + let req = Request::builder() + .method(Method::GET) + .header(header::ACCEPT, HeaderValue::from_static(APPLICATION_JSON)) + .header( + crate::proxy::X_AUTH_PAYLOAD, + HeaderValue::from_str(&serialized_proxy_sign).unwrap(), + ) + .body(Body::empty()) + .unwrap(); + + let (mut req, deserialized_proxy_sign) = + crate::proxy::parse_auth_header(req).await.unwrap(); + + let body_bytes = hyper::body::to_bytes(req.body_mut()).await.unwrap(); + assert!( + body_bytes.is_empty(), + "Body should be empty for GET methods" + ); + + assert_eq!(deserialized_proxy_sign, proxy_sign); + assert!(deserialized_proxy_sign.is_valid_message()); + + let additional_headers = &[ + header::CONTENT_LENGTH, + HeaderName::from_bytes(X_AUTH_PAYLOAD.as_bytes()).unwrap(), + ]; + remove_hop_by_hop_headers(&mut req, additional_headers).unwrap(); + } + + #[tokio::test] + async fn test_modify_request_uri() { + use crate::proxy::ProxyType; + use std::str::FromStr; + + const EXPECTED_URI: &str = "http://localhost:8000/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC"; + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/nft-test/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/nft-test").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!( + req.uri(), + "http://localhost:8000/nft/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC" + ); + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/nft-test/special/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/nft-test/special").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!(req.uri(), EXPECTED_URI); + + let mut req = Request::builder() + .uri("https://komodo.proxy:5535/api/v2.2/0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326/nft/transfers?chain=eth&format=decimal&order=DESC") + .body(Body::empty()) + .unwrap(); + let proxy_route = ProxyRoute { + inbound_route: String::from_str("/").unwrap(), + outbound_route: "http://localhost:8000".to_string(), + proxy_type: ProxyType::Moralis, + authorized: false, + allowed_rpc_methods: vec![], + rate_limiter: None, + }; + modify_request_uri(&mut req, &proxy_route).unwrap(); + assert_eq!(req.uri(), EXPECTED_URI); + } } From d5f7ae0eeda514f9edaa3c9d0c246402dd919bfe Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:11:38 +0300 Subject: [PATCH 16/23] remove .TODO file Signed-off-by: onur-ozkan --- .TODO | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .TODO diff --git a/.TODO b/.TODO deleted file mode 100644 index ae4b42d..0000000 --- a/.TODO +++ /dev/null @@ -1 +0,0 @@ -- Peer verification through KDF RPCs From 327c0d1b4af6527fdd43fd23d2bc61c28fcea62d Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:12:37 +0300 Subject: [PATCH 17/23] add TODO to README Signed-off-by: onur-ozkan --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f30fa1d..ecfda32 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,13 @@ Expose configuration file's path as an environment variable in `AUTH_APP_CONFIG_ ***Important Note:*** The environment where the application will be deployed, the timezone MUST be as UTC. Also, make sure redis is version `6.*` -### Architecture +### Architecture (TODO: OUTDATED) ![arch2](https://github.com/KomodoPlatform/komodo-defi-proxy/assets/39852038/be7fe7ae-2f2a-4f68-afa8-ce4938c570a7) -**Execution flow:** +**Execution flow (TODO: OUTDATED):** + 1) Client sends the request. 2) Redirect either to websocket or http handler. From d689e29fe68acf696e9eb697fca87a150c6b0869 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:17:48 +0300 Subject: [PATCH 18/23] move `JEMALLOC_SYS_WITH_MALLOC_CONF` to cargo config Signed-off-by: onur-ozkan --- .cargo/config.toml | 3 +++ Containerfile | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 04e8f2d..5285365 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,6 @@ +[env] +JEMALLOC_SYS_WITH_MALLOC_CONF = "background_thread:true,narenas:1,tcache:false,dirty_decay_ms:0,muzzy_decay_ms:0,metadata_thp:auto" + [target.'cfg(all())'] rustflags = [ "-Dunsafe_code" diff --git a/Containerfile b/Containerfile index 9d749bc..683266f 100644 --- a/Containerfile +++ b/Containerfile @@ -9,9 +9,6 @@ RUN apt-get update \ RUN curl https://sh.rustup.rs -sSf | bash -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" -## Jemalloc tweaks -ENV JEMALLOC_SYS_WITH_MALLOC_CONF="background_thread:true,narenas:1,tcache:false,dirty_decay_ms:0,muzzy_decay_ms:0,metadata_thp:auto" - COPY . . RUN cargo build --release From 6fd751327fa74136618f053e5bee04de77cad9d9 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:19:34 +0300 Subject: [PATCH 19/23] add optimum build optimizations for dev/release profiles Signed-off-by: onur-ozkan --- Cargo.toml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 61158b6..3e16297 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,21 @@ jemallocator = "0.5.0" [dev-dependencies] hex = "0.4.3" + +[profile.release] +debug = 0 +debug-assertions = false +opt-level = 3 +strip = true +codegen-units = 1 +lto = true +panic = "abort" + +[profile.dev] +opt-level = 0 +debug = 1 +debug-assertions = false +panic = 'unwind' +incremental = true +codegen-units = 256 + From dccdc6e389f1f902692db2018f89a3512ffd48d6 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:23:23 +0300 Subject: [PATCH 20/23] upgrade to stable 1.80 Signed-off-by: onur-ozkan --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c86325a..e275529 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] components = [ "rustfmt", "clippy", "rust-analyzer"] -channel = "1.75" +channel = "1.80" From 09bc5c6f32d6793e824c8db0b7c6a6072b61542f Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 14:28:50 +0300 Subject: [PATCH 21/23] use the remote source Signed-off-by: onur-ozkan --- Cargo.lock | 1 + Cargo.toml | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2071262..9861d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1404,6 +1404,7 @@ dependencies = [ [[package]] name = "proxy_signature" version = "0.1.0" +source = "git+https://github.com/KomodoPlatform/komodo-defi-framework?rev=9ebc006#9ebc0065350c735d5ba209f634f48a9e24063243" dependencies = [ "chrono", "http", diff --git a/Cargo.toml b/Cargo.toml index 3e16297..24dfac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,8 @@ simple_logger = "2.1.0" tokio = { version = "1.12.0", default-features = false, features = ["macros", "rt-multi-thread", "sync", "time"] } tokio-tungstenite = { version = "0.20.0", features = ["native-tls"] } # From our sources -# proxy_signature = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", rev = "0784f1a" } libp2p = { git = "https://github.com/KomodoPlatform/rust-libp2p.git", tag = "k-0.52.4", default-features = false, features = ["identify"] } -proxy_signature = { path = "/home/nimda/devspace/KomodoPlatform/komodo-defi-framework/mm2src/proxy_signature" } +proxy_signature = { git = "https://github.com/KomodoPlatform/komodo-defi-framework", rev = "9ebc006" } [target.x86_64-unknown-linux-gnu.dependencies] jemallocator = "0.5.0" From 8fa2b9c8804945d8d479bbcff4d42603c1c0fb88 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 17:36:07 +0300 Subject: [PATCH 22/23] remove leftover println macros Signed-off-by: onur-ozkan --- src/proxy/mod.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/proxy/mod.rs b/src/proxy/mod.rs index 76e4ed9..cf42f1e 100644 --- a/src/proxy/mod.rs +++ b/src/proxy/mod.rs @@ -174,19 +174,14 @@ where .get(X_AUTH_PAYLOAD) .ok_or("Missing X-Auth-Payload header")? .to_str()?; - println!("HEADER VALUE {:?}", header_value); let proxy_sign: ProxySign = serde_json::from_str(header_value)?; - println!("HEADER VALUE {:?}", header_value); let body_bytes = hyper::body::to_bytes(body).await?; if body_bytes.is_empty() { return Err("Empty body cannot be deserialized into non-optional type T".into()); } let payload: serde_json::Value = serde_json::from_slice(&body_bytes)?; - println!("AAAAAAAAAAA {:?}", payload); let payload: T = serde_json::from_slice(&body_bytes)?; - println!("AAAAAAAAAAAAAAAAAAAAAAAA"); let new_req = Request::from_parts(parts, Body::from(body_bytes)); - println!("WOOOOOOOOOOOOOOO"); Ok((new_req, payload, proxy_sign)) } From 9078a6773b820d2c97ecfd7a852ca31a2efd790a Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 1 Aug 2024 17:40:39 +0300 Subject: [PATCH 23/23] rename `Cosmos` to `BlockPi` Signed-off-by: onur-ozkan --- src/proxy/mod.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/proxy/mod.rs b/src/proxy/mod.rs index cf42f1e..2dcb9b0 100644 --- a/src/proxy/mod.rs +++ b/src/proxy/mod.rs @@ -30,7 +30,7 @@ const KEEP_ALIVE: &str = "keep-alive"; pub(crate) enum ProxyType { Quicknode, Moralis, - Cosmos, + BlockPi, } /// Represents the types of payloads that can be processed by the proxy, with each variant tailored to a specific proxy type. @@ -44,8 +44,8 @@ pub(crate) enum PayloadData { }, /// Moralis feature requires only Signed Message in X-Auth-Payload header and doesn't have body Moralis(ProxySign), - /// Cosmos feature requires body payload and Signed Message in X-Auth-Payload header - Cosmos { + /// BlockPi feature requires body payload and Signed Message in X-Auth-Payload header + BlockPi { payload: RpcPayload, proxy_sign: ProxySign, }, @@ -57,7 +57,7 @@ impl PayloadData { match self { PayloadData::Quicknode { proxy_sign, .. } => proxy_sign, PayloadData::Moralis(proxy_sign) => proxy_sign, - PayloadData::Cosmos { proxy_sign, .. } => proxy_sign, + PayloadData::BlockPi { proxy_sign, .. } => proxy_sign, } } } @@ -82,9 +82,9 @@ pub(crate) async fn generate_payload_from_req( let (req, proxy_sign) = parse_auth_header(req).await?; Ok((req, PayloadData::Moralis(proxy_sign))) } - ProxyType::Cosmos => { + ProxyType::BlockPi => { let (req, payload, proxy_sign) = parse_body_and_auth_header::(req).await?; - let payload_data = PayloadData::Cosmos { + let payload_data = PayloadData::BlockPi { payload, proxy_sign, }; @@ -128,7 +128,7 @@ pub(crate) async fn proxy( ) .await } - PayloadData::Cosmos { + PayloadData::BlockPi { payload, proxy_sign, } => { @@ -179,7 +179,6 @@ where if body_bytes.is_empty() { return Err("Empty body cannot be deserialized into non-optional type T".into()); } - let payload: serde_json::Value = serde_json::from_slice(&body_bytes)?; let payload: T = serde_json::from_slice(&body_bytes)?; let new_req = Request::from_parts(parts, Body::from(body_bytes)); Ok((new_req, payload, proxy_sign))