From c19aee32ab891efa600fddcb90be884ca4de7d00 Mon Sep 17 00:00:00 2001 From: iota9star Date: Tue, 19 Mar 2024 19:28:04 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=96=20Release=20v0.1.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 11 +- CHANGELOG.md | 4 + Cargo.lock | 571 +++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 10 +- README-ZH.md | 11 + README.md | 13 +- src/cache.rs | 21 ++ src/envs.rs | 75 +++++++ src/main.rs | 190 +++++++++-------- src/urn.rs | 231 ++++++++++++--------- 10 files changed, 921 insertions(+), 216 deletions(-) create mode 100644 src/cache.rs create mode 100644 src/envs.rs diff --git a/.env b/.env index f522cc1..05c57f5 100644 --- a/.env +++ b/.env @@ -10,7 +10,14 @@ IP_LIMIT_BURST_SIZE=50000 # default 500 CONCURRENCY_LIMIT=100 # default 1 -ELECTRUMX_WS_INSTANCE=5 -# default 10 +ELECTRUMX_WS_INSTANCE=1 +# default 10s RESPONSE_TIMEOUT=10 +# max cache entry, default 10000 +MAX_CACHE_ENTRIES=10000 +# cache live time, default 480s +CACHE_TIME_TO_LIVE=480 +# cache idle time, default 60s +CACHE_TIME_TO_IDLE=60 + RUST_LOG=info \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 63cbbaf..cbca5a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.6 + +- Added support for request caching to reduce indexer access and improve performance. + ## 0.1.5 - Allow configuring the number of concurrently running WebSocket instances to improve throughput. diff --git a/Cargo.lock b/Cargo.lock index 4ca42ed..9045ad1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,15 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + [[package]] name = "async-trait" version = "0.1.77" @@ -59,10 +68,10 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.1.0", "hyper-util", "itoa", "matchit", @@ -92,8 +101,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -125,6 +134,42 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "bech32" +version = "0.10.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" + +[[package]] +name = "bitcoin" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd00f3c09b5f21fb357abe32d29946eb8bb7a0862bae62c0b5e4a692acbbe73c" +dependencies = [ + "bech32", + "bitcoin-internals", + "bitcoin_hashes", + "hex-conservative", + "hex_lit", + "secp256k1", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -152,6 +197,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "byteorder" version = "1.5.0" @@ -164,6 +215,37 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.0.83" @@ -204,6 +286,24 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.18" @@ -273,16 +373,20 @@ version = "0.1.5" dependencies = [ "anyhow", "axum", + "bitcoin", "bytes", "dotenv", "forwarded-header-value", "futures", "headers", + "hex", "http-body-util", + "moka", "once_cell", "openssl", "rand", "regex", + "reqwest", "serde", "serde_json", "time", @@ -298,6 +402,15 @@ dependencies = [ "url", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -314,6 +427,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "2.0.1" @@ -482,6 +610,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "governor" version = "0.6.0" @@ -495,11 +629,30 @@ dependencies = [ "no-std-compat", "nonzero_ext", "parking_lot", - "quanta", + "quanta 0.11.1", "rand", "smallvec", ] +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.0" @@ -511,7 +664,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 1.0.0", "indexmap 2.1.0", "slab", "tokio", @@ -550,7 +703,7 @@ dependencies = [ "base64", "bytes", "headers-core", - "http", + "http 1.0.0", "httpdate", "mime", "sha1", @@ -562,7 +715,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.0.0", ] [[package]] @@ -571,6 +724,35 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.0.0" @@ -582,6 +764,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -589,7 +782,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.0.0", ] [[package]] @@ -600,8 +793,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -617,6 +810,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.24", + "http 0.2.11", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.1.0" @@ -626,9 +843,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.0", + "http 1.0.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -636,6 +853,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.28", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-util" version = "0.1.2" @@ -645,9 +875,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", "pin-project-lite", "socket2", "tokio", @@ -684,6 +914,12 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itoa" version = "1.0.10" @@ -780,6 +1016,30 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "moka" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1911e88d5831f748a4097a43862d129e3c6fca831eecac9b8db6d01d93c9de2" +dependencies = [ + "async-lock", + "async-trait", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "futures-util", + "once_cell", + "parking_lot", + "quanta 0.12.2", + "rustc_version", + "skeptic", + "smallvec", + "tagptr", + "thiserror", + "triomphe", + "uuid", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -862,9 +1122,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -903,9 +1163,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" dependencies = [ "cc", "libc", @@ -1008,6 +1268,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +dependencies = [ + "bitflags 2.4.1", + "memchr", + "unicase", +] + [[package]] name = "quanta" version = "0.11.1" @@ -1018,7 +1289,22 @@ dependencies = [ "libc", "mach2", "once_cell", - "raw-cpuid", + "raw-cpuid 10.7.0", + "wasi", + "web-sys", + "winapi", +] + +[[package]] +name = "quanta" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca0b7bac0b97248c40bb77288fc52029cf1459c0461ea1b05ee32ccf011de2c" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid 11.0.1", "wasi", "web-sys", "winapi", @@ -1072,6 +1358,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "raw-cpuid" +version = "11.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" +dependencies = [ + "bitflags 2.4.1", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1083,9 +1378,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -1095,9 +1390,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -1110,12 +1405,59 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.24", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.28" @@ -1141,6 +1483,15 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.23" @@ -1156,6 +1507,25 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "secp256k1" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f622567e3b4b38154fb8190bcf6b160d7a4301d70595a49195b48c116007a27" +dependencies = [ + "bitcoin_hashes", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -1179,6 +1549,15 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + [[package]] name = "serde" version = "1.0.195" @@ -1261,6 +1640,21 @@ dependencies = [ "libc", ] +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "slab" version = "0.4.9" @@ -1303,6 +1697,33 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tempfile" version = "3.9.0" @@ -1488,8 +1909,8 @@ dependencies = [ "bitflags 2.4.1", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "pin-project-lite", "tower-layer", @@ -1511,21 +1932,17 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tower_governor" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a54c8af9e56c11e47b70dc8504e81d362783e2fd8e7373938d632954841126" +checksum = "0d31d2cd0776b0e10664d3db2e362b9a8b38a18cb09ba97d3f2f775c54f2c51b" dependencies = [ "axum", "forwarded-header-value", - "futures", - "futures-core", "governor", - "http", + "http 1.0.0", "pin-project", "thiserror", - "tokio", "tower", - "tower-layer", "tracing", ] @@ -1587,6 +2004,18 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tungstenite" version = "0.21.0" @@ -1596,7 +2025,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.0.0", "httparse", "log", "native-tls", @@ -1613,6 +2042,15 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.14" @@ -1651,6 +2089,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" @@ -1669,6 +2116,25 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1700,6 +2166,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.89" @@ -1755,6 +2233,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1892,3 +2379,13 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/Cargo.toml b/Cargo.toml index 270d5b3..419e0ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,11 @@ futures = "^0" serde = { version = "^1.0.195", features = ["derive"] } serde_json = "^1" tokio = { version = "^1", features = ["full"] } +reqwest = { version = "0.11", features = ["json"] } tokio-stream = "^0" tungstenite = "^0" tokio-tungstenite = { version = "^0", features = ["native-tls"] } -openssl = { version = "^0.10", features = ["vendored"] } +openssl = { version = "^0.10.63", features = ["vendored"] } url = "^2" time = { version = "^0", features = [] } tower = { version = "^0", features = ["full"] } @@ -23,14 +24,17 @@ once_cell = "^1" tracing = "^0" tracing-subscriber = "^0" anyhow = "^1" -tower_governor = "^0.3" +tower_governor = "^0.3.1" bytes = "^1" http-body-util = "^0" dotenv = "^0" -regex = "^1" +regex = "^1.10.3" headers = "0.4.0" forwarded-header-value = "0.1.1" rand = "0.8.5" +bitcoin = "0.31.1" +hex = "0.4.3" +moka = { version = "0.12.5", features = ["future"] } [profile.release] strip = true diff --git a/README-ZH.md b/README-ZH.md index a71a921..5613d85 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -32,6 +32,14 @@ ELECTRUMX_WS_INSTANCE=5 CONCURRENCY_LIMIT=500 # 默认 10,接收 WebSocket 消息的超时时间 RESPONSE_TIMEOUT=10 + +# 默认 10000, 最大的缓存数量 +MAX_CACHE_ENTRIES=10000 +# 默认 480s, 缓存最大存活时间 +CACHE_TIME_TO_LIVE=480 +# 默认 60s, 缓存空闲时间,如果没有访问,缓存将被移除 +CACHE_TIME_TO_IDLE=60 + RUST_LOG=info ``` @@ -45,6 +53,9 @@ RUST_LOG=info - `ELECTRUMX_WS_INSTANCE`:同时运行的 ws 实例,可以提高吞吐量,按需设置。 - `CONCURRENCY_LIMIT`:允许的最大并发连接数。 - `RESPONSE_TIMEOUT`:接收 WebSocket 消息的超时时间。 +- `MAX_CACHE_ENTRIES`:最大的缓存数量。 +- `CACHE_TIME_TO_LIVE`:缓存最大存活时间。 +- `CACHE_TIME_TO_IDLE`:缓存空闲时间,如果没有访问,缓存将被移除。 - `RUST_LOG`:Rust 日志框架的日志级别。选项包括 `trace`、`debug`、`info`、`warn` 和 `error`。 #### 使用 diff --git a/README.md b/README.md index 8e29efe..9df83d8 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,16 @@ IP_LIMIT_BURST_SIZE=10 ELECTRUMX_WS_INSTANCE=5 # Default 500, maximum concurrent connections CONCURRENCY_LIMIT=500 -# Default 10, timeout for receiving WebSocket messages +# Default 10s, timeout for receiving WebSocket messages RESPONSE_TIMEOUT=10 + +# Default 10000, max cache entry +MAX_CACHE_ENTRIES=10000 +# Default 480s, cache max live time +CACHE_TIME_TO_LIVE=480 +# Default 60s, cache idle time, if no access, cache will be removed +CACHE_TIME_TO_IDLE=60 + RUST_LOG=info ``` @@ -45,6 +53,9 @@ Adjust these values as needed. Here's a brief explanation of the configuration p - `ELECTRUMX_WS_INSTANCE`: Concurrently running ws instances, can improve throughput, set as needed. - `CONCURRENCY_LIMIT`: Maximum allowed concurrent connections. - `RESPONSE_TIMEOUT`: Timeout for receiving WebSocket messages. +- `MAX_CACHE_ENTRIES`: Maximum cache entry. +- `CACHE_TIME_TO_LIVE`: Cache max live time. +- `CACHE_TIME_TO_IDLE`: Cache idle time, if no access, cache will be removed. - `RUST_LOG`: Log level for Rust logging framework. Options include `trace`, `debug`, `info`, `warn`, and `error`. #### Usage diff --git a/src/cache.rs b/src/cache.rs new file mode 100644 index 0000000..2af905a --- /dev/null +++ b/src/cache.rs @@ -0,0 +1,21 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; + +use serde_json::Value; + +pub fn to_cache_key(method: &str, params: &[Value]) -> u64 { + let mut hasher = DefaultHasher::new(); + method.hash(&mut hasher); + for x in params.iter() { + if x.is_string() { + x.as_str().unwrap().hash(&mut hasher); + } else if x.is_number() { + // never use f64 as a key + x.as_i64().unwrap().hash(&mut hasher); + } else if x.is_boolean() { + x.as_bool().unwrap().hash(&mut hasher); + } else { + x.to_string().hash(&mut hasher); + } + } + hasher.finish() +} diff --git a/src/envs.rs b/src/envs.rs new file mode 100644 index 0000000..f2e9cc1 --- /dev/null +++ b/src/envs.rs @@ -0,0 +1,75 @@ + + +use std::env; +use std::sync::LazyLock; + +pub static IP_LIMIT_PER_MILLS: LazyLock = LazyLock::new(|| { + let per_second: u64 = env::var("IP_LIMIT_PER_SECOND") + .unwrap_or("0".to_string()) + .parse() + .unwrap(); + if per_second > 0 { + per_second * 1000 + } else { + env::var("IP_LIMIT_PER_MILLS") + .unwrap_or("10".to_string()) + .parse() + .unwrap() + } +}); + +pub static IP_LIMIT_BURST_SIZE: LazyLock = LazyLock::new(|| { + env::var("IP_LIMIT_BURST_SIZE") + .unwrap_or("10".to_string()) + .parse() + .unwrap() +}); + +pub static CONCURRENCY_LIMIT: LazyLock = LazyLock::new(|| { + env::var("CONCURRENCY_LIMIT") + .unwrap_or("500".to_string()) + .parse() + .unwrap() +}); + +pub static ELECTRUMX_WSS: LazyLock = LazyLock::new(|| { + env::var("ELECTRUMX_WSS").unwrap_or("wss://electrumx.atomicals.xyz:50012".to_string()) +}); + +pub static ELECTRUMX_WS_INSTANCE: LazyLock = LazyLock::new(|| { + env::var("ELECTRUMX_WS_INSTANCE") + .unwrap_or("1".to_string()) + .parse() + .unwrap() +}); + +pub static PROXY_HOST: LazyLock = + LazyLock::new(|| env::var("PROXY_HOST").unwrap_or("0.0.0.0:12321".into())); + +pub static RESPONSE_TIMEOUT: LazyLock = LazyLock::new(|| { + env::var("RESPONSE_TIMEOUT") + .unwrap_or("10".to_string()) + .parse() + .unwrap() +}); + +pub static MAX_CACHE_ENTRIES: LazyLock = LazyLock::new(|| { + env::var("MAX_CACHE_ENTRIES") + .unwrap_or("10000".to_string()) + .parse() + .unwrap() +}); + +pub static CACHE_TIME_TO_LIVE: LazyLock = LazyLock::new(|| { + env::var("CACHE_TIME_TO_LIVE") + .unwrap_or("480".to_string()) + .parse() + .unwrap() +}); + +pub static CACHE_TIME_TO_IDLE: LazyLock = LazyLock::new(|| { + env::var("CACHE_TIME_TO_IDLE") + .unwrap_or("60".to_string()) + .parse() + .unwrap() +}); \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d62908c..3858e74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,47 +2,57 @@ use std::any::Any; use std::collections::HashMap; -use std::env; use std::net::SocketAddr; -use std::sync::{Arc, LazyLock}; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; +use std::sync::Arc; use std::time::Duration; use axum::body::Body; -use axum::extract::{Path, Query}; use axum::extract::Extension; use axum::extract::Json; +use axum::extract::{Path, Query}; use axum::http; -use axum::http::{header, HeaderMap}; use axum::http::StatusCode; +use axum::http::{header, HeaderMap}; use axum::response::IntoResponse; use axum::response::Response; -use axum::Router; use axum::routing::get; +use axum::Router; use bytes::Bytes; use dotenv::dotenv; use futures::{SinkExt, StreamExt}; use http_body_util::Full; +use moka::future::Cache; use once_cell::sync::Lazy; use rand::Rng; use serde::{Deserialize, Serialize}; use serde_json::{json, Number, Value}; -use tokio::sync::{mpsc, Mutex, RwLock}; -use tokio::sync::oneshot; +use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::{mpsc, oneshot}; +use tokio::sync::{Mutex, RwLock}; use tokio_stream::wrappers::UnboundedReceiverStream; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::Message; use tower::limit::ConcurrencyLimitLayer; use tower_governor::governor::GovernorConfigBuilder; -use tower_governor::GovernorLayer; use tower_governor::key_extractor::SmartIpKeyExtractor; +use tower_governor::GovernorLayer; use tower_http::catch_panic::CatchPanicLayer; use tower_http::cors::CorsLayer; use tower_http::trace::TraceLayer; use tracing::{error, info, warn}; +use crate::cache::to_cache_key; +use crate::envs::{ + CACHE_TIME_TO_IDLE, CACHE_TIME_TO_LIVE, CONCURRENCY_LIMIT, ELECTRUMX_WSS, + ELECTRUMX_WS_INSTANCE, IP_LIMIT_BURST_SIZE, IP_LIMIT_PER_MILLS, MAX_CACHE_ENTRIES, PROXY_HOST, + RESPONSE_TIMEOUT, +}; use crate::ip::maybe_ip_from_headers; +use crate::urn::{handle_urn, handle_urn_with_res}; +mod cache; +mod envs; mod ip; mod urn; @@ -60,7 +70,7 @@ struct JsonRpcResponse { id: u32, } -#[derive(Serialize)] +#[derive(Serialize, Clone)] struct R { success: bool, #[serde(skip_serializing_if = "Option::is_none")] @@ -103,56 +113,6 @@ impl R { } } -static IP_LIMIT_PER_MILLS: LazyLock = LazyLock::new(|| { - let per_second:u64 = env::var("IP_LIMIT_PER_SECOND") - .unwrap_or("0".to_string()) - .parse() - .unwrap(); - if per_second > 0 { - per_second * 1000 - } else { - env::var("IP_LIMIT_PER_MILLS") - .unwrap_or("10".to_string()) - .parse() - .unwrap() - } -}); - -static IP_LIMIT_BURST_SIZE: LazyLock = LazyLock::new(|| { - env::var("IP_LIMIT_BURST_SIZE") - .unwrap_or("10".to_string()) - .parse() - .unwrap() -}); - -static CONCURRENCY_LIMIT: LazyLock = LazyLock::new(|| { - env::var("CONCURRENCY_LIMIT") - .unwrap_or("500".to_string()) - .parse() - .unwrap() -}); - -static ELECTRUMX_WSS: LazyLock = LazyLock::new(|| { - env::var("ELECTRUMX_WSS").unwrap_or("wss://electrumx.atomicals.xyz:50012".to_string()) -}); - -static ELECTRUMX_WS_INSTANCE: LazyLock = LazyLock::new(|| { - env::var("ELECTRUMX_WS_INSTANCE") - .unwrap_or("1".to_string()) - .parse() - .unwrap() -}); - -static PROXY_HOST: LazyLock = - LazyLock::new(|| env::var("PROXY_HOST").unwrap_or("0.0.0.0:12321".into())); - -static RESPONSE_TIMEOUT: LazyLock = LazyLock::new(|| { - env::var("RESPONSE_TIMEOUT") - .unwrap_or("10".to_string()) - .parse() - .unwrap() -}); - // The use of `AtomicU32` is to ensure not exceeding the integer range of other systems. static ID_COUNTER: Lazy = Lazy::new(|| AtomicU32::new(0)); @@ -185,7 +145,8 @@ impl IntoResponse for R { } async fn handle_get( - Extension(callbacks): Extension, Callbacks)>>, + Extension(callbacks): Extension, Callbacks)>>, + Extension(cache): Extension, headers: HeaderMap, Path(method): Path, Query(query): Query, @@ -194,29 +155,30 @@ async fn handle_get( let sender = item.0.clone(); let calls = item.1.clone(); let r = match query.get("params") { - None => handle_request(sender, calls, headers, method, vec![]).await, + None => handle_request(cache, sender, calls, headers, method, vec![]).await, Some(v) => { let x = v .as_str() .map(|s| if s.is_empty() { "[]" } else { s }) .unwrap(); let params = serde_json::from_str(x).unwrap(); - handle_request(sender, calls, headers, method, params).await + handle_request(cache, sender, calls, headers, method, params).await } }; Ok(r) } fn random_callback( - callbacks: &[(mpsc::UnboundedSender, Callbacks)], -) -> &(mpsc::UnboundedSender, Callbacks) { + callbacks: &[(UnboundedSender, Callbacks)], +) -> &(UnboundedSender, Callbacks) { let mut rng = rand::thread_rng(); let index = rng.gen_range(0..callbacks.len()); &callbacks[index] } async fn handle_post( - Extension(callbacks): Extension, Callbacks)>>, + Extension(callbacks): Extension, Callbacks)>>, + Extension(cache): Extension, headers: HeaderMap, Path(method): Path, body: Option>, @@ -225,12 +187,12 @@ async fn handle_post( let sender = item.0.clone(); let calls = item.1.clone(); let r = match body { - None => handle_request(sender, calls, headers, method, vec![]).await, + None => handle_request(cache, sender, calls, headers, method, vec![]).await, Some(v) => match v.0.get("params") { - None => handle_request(sender, calls, headers, method, vec![]).await, + None => handle_request(cache, sender, calls, headers, method, vec![]).await, Some(v) => { let x = v.as_array().unwrap(); - handle_request(sender, calls, headers, method, x.clone()).await + handle_request(cache, sender, calls, headers, method, x.clone()).await } }, }; @@ -238,7 +200,8 @@ async fn handle_post( } async fn handle_request( - ws_tx: mpsc::UnboundedSender, + cache: MokaCache, + ws_tx: UnboundedSender, callbacks: Callbacks, headers: HeaderMap, method: String, @@ -246,10 +209,19 @@ async fn handle_request( ) -> R { let id = get_next_id(); let addr = maybe_ip_from_headers(&headers); - info!( - "{} => {}, {}({:?})", - &addr, &id, &method, ¶ms - ); + let cache_key = to_cache_key(&method, ¶ms); + let use_cache = method != "blockchain.atomicals.get_global"; + if use_cache { + let cache_value = cache.get(&cache_key).await; + if let Some(v) = cache_value { + info!( + "{} => {}, {}({:?}) matched cache({})", + &addr, &id, &method, ¶ms, &cache_key + ); + return v.clone(); + } + } + info!("{} => {}, {}({:?})", &addr, &id, &method, ¶ms); let (response_tx, response_rx) = oneshot::channel(); { callbacks.write().await.insert(id, response_tx); @@ -259,7 +231,11 @@ async fn handle_request( match tokio::time::timeout(Duration::from_secs(*RESPONSE_TIMEOUT), response_rx).await { Ok(Ok(rep)) => { if let Some(result) = rep.result { - R::ok(result) + let r = R::ok(result); + if use_cache { + cache.insert(cache_key, r.clone()).await; + } + r } else if let Some(err) = rep.error { let err = err.as_object().unwrap(); R { @@ -287,7 +263,7 @@ async fn handle_request( } async fn handle_health( - Extension(callbacks): Extension, Callbacks)>>, + Extension(callbacks): Extension, Callbacks)>>, headers: HeaderMap, ) -> impl IntoResponse { let id = get_next_id(); @@ -331,7 +307,7 @@ async fn handle_proxy() -> impl IntoResponse { "GET": "GET /proxy/:method?params=[\"value1\"] with string encoded array in the query argument \"params\" in the URL." }, "healthCheck": "GET /proxy/health", - "github": "https://github.com/AstroxNetwork/elex-proxy", + "github": "https://github.com/WizzWallet/elex-proxy", "license": "MIT" } })) @@ -356,6 +332,8 @@ fn handle_panic(err: Box) -> http::Response; + #[tokio::main] async fn main() { dotenv().ok(); @@ -375,6 +353,11 @@ async fn main() { calls.push((ws_tx, callbacks.clone())); try_new_client(i, callbacks, ws_rx_stream); } + let cache: MokaCache = Cache::builder() + .max_capacity(*MAX_CACHE_ENTRIES) + .time_to_live(Duration::from_secs(*CACHE_TIME_TO_LIVE)) + .time_to_idle(Duration::from_secs(*CACHE_TIME_TO_IDLE)) + .build(); let app = Router::new() .fallback(|uri: http::Uri| async move { let body = R::error(-1, format!("No route: {}", &uri)); @@ -386,6 +369,8 @@ async fn main() { .unwrap() }) .route("/", get(|| async { "Hello, Atomicals!" })) + // .route("/urn/:urn/:res", get(handle_urn_with_res)) + // .route("/urn/:urn", get(handle_urn)) .route("/proxy", get(handle_proxy).post(handle_proxy)) .route("/proxy/health", get(handle_health).post(handle_health)) .route("/proxy/:method", get(handle_get).post(handle_post)) @@ -396,7 +381,49 @@ async fn main() { .layer(CatchPanicLayer::custom(handle_panic)) .layer(TraceLayer::new_for_http()) .layer(CorsLayer::permissive()) - .layer(Extension(calls.clone())); + .layer(Extension(calls.clone())) + .layer(Extension(cache.clone())); + let block_height = AtomicU64::new(0); + tokio::spawn(async move { + loop { + let vec1 = calls.clone(); + let callback = random_callback(&vec1); + let r = handle_request( + cache.clone(), + callback.0.clone(), + callback.1.clone(), + HeaderMap::new(), + "blockchain.atomicals.get_global".into(), + vec![], + ) + .await; + if let Some(v) = r.response { + if v.is_object() { + let height = v + .as_object() + .unwrap() + .get("global") + .unwrap() + .as_object() + .unwrap() + .get("height") + .unwrap() + .as_u64() + .unwrap(); + if block_height.load(Ordering::SeqCst) != height { + block_height.store(height, Ordering::SeqCst); + info!("New block height: {}, invalidate all cache", height); + // for i in 0..12 { + // tokio::time::sleep(Duration::from_secs(10)).await; + // info!("Invalidate all cache...{}", i); + // cache.invalidate_all(); + // } + } + } + } + tokio::time::sleep(Duration::from_secs(10)).await; + } + }); let listener = tokio::net::TcpListener::bind((*PROXY_HOST).clone()) .await .unwrap(); @@ -410,7 +437,7 @@ async fn main() { } fn new_callbacks() -> ( - mpsc::UnboundedSender, + UnboundedSender, Callbacks, Arc>>, ) { @@ -456,7 +483,10 @@ fn try_new_client( info!("WS-{} <= {}, Request matched", ins, &resp.id); let _ = callback.send(resp); } else { - warn!("WS-{} <= {}, No matching request found", ins, &resp.id); + warn!( + "WS-{} <= {}, No matching request found", + ins, &resp.id + ); } } else { error!("WS-{} Failed to parse ws response: {}", ins, text); diff --git a/src/urn.rs b/src/urn.rs index 36d22df..4d0c986 100644 --- a/src/urn.rs +++ b/src/urn.rs @@ -1,93 +1,138 @@ -// use anyhow::{anyhow, Result}; -// use axum::Extension; -// use axum::extract::Path; -// use headers::HeaderMap; -// use regex::Regex; -// use serde_json::Value; -// use tokio::sync::mpsc; -// use tracing::info; -// -// use crate::{AppError, Callbacks, handle_request, JsonRpcRequest, R}; -// -// async fn handle_urn( -// Extension(callbacks): Extension, -// Extension(ws_tx): Extension>, -// headers:HeaderMap, -// Path(urn): Path, -// ) -> std::result::Result { -// let result = decode_urn(&urn).unwrap(); -// info!("URN info: {:?}", result); -// match result.urn_type { -// UrnType::AtomicalId => { -// Ok(R::ok(Value::Null)) -// } -// UrnType::Realm => { -// let r = handle_request(ws_tx, callbacks, headers, "blockchain.atomicals.get_by_realm".into(), vec![Value::String(result.identifier)]).await; -// if let Some(v) = r.response { -// let atomical_id = v.as_object().unwrap().get("result").unwrap().as_object().unwrap().get("atomical_id").unwrap().as_str(); -// // if let Some(v) = atomical_id{ -// // -// // }else { -// // Ok(R::error(-1,format!("realm not found: {}", &result.identifier))) -// // } -// Ok(R::ok(Value::Null)) -// }else { -// Ok(r) -// } -// } -// UrnType::Container => { -// Ok(R::ok(Value::Null)) -// } -// UrnType::Dat => { -// Ok(R::ok(Value::Null)) -// } -// UrnType::Arc => { -// Ok(R::ok(Value::Null)) -// } -// } -// } -// -// #[derive(Debug)] -// pub enum UrnType { -// AtomicalId = 0, -// Realm = 1, -// Container = 2, -// Dat = 3, -// Arc = 4, -// } -// -// #[derive(Debug)] -// pub struct UrnInfo { -// pub urn_type: UrnType, -// pub identifier: String, -// pub path_type: Option, -// pub path: Option, -// } -// -// pub fn decode_urn(urn: &str) -> Result { -// if let Some(captures) = Regex::new(r"atom:btc:dat:([0-9a-f]{64}i\d+)(([/$])?.*)")?.captures(urn) { -// return process_match(UrnType::Dat, captures); -// } -// if let Some(captures) = Regex::new(r"atom:btc:id:([0-9a-f]{64}i\d+)(([/$])?.*)")?.captures(urn) { -// return process_match(UrnType::AtomicalId, captures); -// } -// if let Some(captures) = Regex::new(r"atom:btc:realm:(.+)(([/$])?.*)")?.captures(urn) { -// return process_match(UrnType::Realm, captures); -// } -// if let Some(captures) = Regex::new(r"atom:btc:container:(.+)(([/$])?.*)")?.captures(urn) { -// return process_match(UrnType::Container, captures); -// } -// if let Some(captures) = Regex::new(r"atom:btc:arc:(.+)(([/$])?.*)")?.captures(urn) { -// return process_match(UrnType::Arc, captures); -// } -// Err(anyhow!("Invalid URN")) -// } -// -// fn process_match(urn_type: UrnType, captures: regex::Captures) -> Result { -// Ok(UrnInfo { -// urn_type, -// identifier: captures[1].to_string(), -// path_type: Some(captures.get(3).map_or("", |m| m.as_str()).to_string()), -// path: captures.get(2).map(|m| m.as_str().to_string()), -// }) -// } +use anyhow::{anyhow, Result}; +use axum::Extension; +use axum::extract::Path; +use headers::HeaderMap; +use regex::Regex; +use serde_json::Value; +use tokio::sync::mpsc; +use tracing::info; + +use crate::{AppError, Callbacks, handle_request, JsonRpcRequest, R, random_callback}; + +pub async fn handle_urn_with_res( + Extension(callbacks): Extension, Callbacks)>>, + headers:HeaderMap, + Path((urn,res)): Path<(String,String)>, +) -> std::result::Result { + info!("URN: {},res: {}", &urn,&res); + let result = decode_urn(&urn).unwrap(); + info!("URN info: {:?}", result); + // match result.urn_type { + // UrnType::AtomicalId => { + // Ok(R::ok(Value::Null)) + // } + // UrnType::Realm => { + // let item = random_callback(&callbacks); + // let sender = item.0.clone(); + // let calls = item.1.clone(); + // let r = handle_request(sender, calls, headers, "blockchain.atomicals.get_by_realm".into(), vec![Value::String(result.identifier)]).await; + // if let Some(v) = r.response { + // let atomical_id = v.as_object().unwrap().get("result").unwrap().as_object().unwrap().get("atomical_id").unwrap().as_str(); + // // if let Some(v) = atomical_id{ + // // + // // }else { + // // Ok(R::error(-1,format!("realm not found: {}", &result.identifier))) + // // } + // Ok(R::ok(Value::Null)) + // }else { + // Ok(r) + // } + // } + // UrnType::Container => { + // Ok(R::ok(Value::Null)) + // } + // UrnType::Dat => { + // Ok(R::ok(Value::Null)) + // } + // UrnType::Arc => { + // Ok(R::ok(Value::Null)) + // } + // } + Ok(R::ok(Value::Null)) +} +pub async fn handle_urn( + Extension(callbacks): Extension, Callbacks)>>, + headers:HeaderMap, + Path(urn): Path, +) -> std::result::Result { + info!("URN: {}", &urn); + let result = decode_urn(&urn).unwrap(); + info!("URN info: {:?}", result); + // match result.urn_type { + // UrnType::AtomicalId => { + // Ok(R::ok(Value::Null)) + // } + // UrnType::Realm => { + // let item = random_callback(&callbacks); + // let sender = item.0.clone(); + // let calls = item.1.clone(); + // let r = handle_request(sender, calls, headers, "blockchain.atomicals.get_by_realm".into(), vec![Value::String(result.identifier)]).await; + // if let Some(v) = r.response { + // let atomical_id = v.as_object().unwrap().get("result").unwrap().as_object().unwrap().get("atomical_id").unwrap().as_str(); + // // if let Some(v) = atomical_id{ + // // + // // }else { + // // Ok(R::error(-1,format!("realm not found: {}", &result.identifier))) + // // } + // Ok(R::ok(Value::Null)) + // }else { + // Ok(r) + // } + // } + // UrnType::Container => { + // Ok(R::ok(Value::Null)) + // } + // UrnType::Dat => { + // Ok(R::ok(Value::Null)) + // } + // UrnType::Arc => { + // Ok(R::ok(Value::Null)) + // } + // } + Ok(R::ok(Value::Null)) +} + +#[derive(Debug)] +pub enum UrnType { + AtomicalId = 0, + Realm = 1, + Container = 2, + Dat = 3, + Arc = 4, +} + +#[derive(Debug)] +pub struct UrnInfo { + pub urn_type: UrnType, + pub identifier: String, + pub path_type: Option, + pub path: Option, +} + +pub fn decode_urn(urn: &str) -> Result { + if let Some(captures) = Regex::new(r"atom:btc:dat:([0-9a-f]{64}i\d+)(([/$])?.*)")?.captures(urn) { + return process_match(UrnType::Dat, captures); + } + if let Some(captures) = Regex::new(r"atom:btc:id:([0-9a-f]{64}i\d+)(([/$])?.*)")?.captures(urn) { + return process_match(UrnType::AtomicalId, captures); + } + if let Some(captures) = Regex::new(r"atom:btc:realm:(.+)(([/$])?.*)")?.captures(urn) { + return process_match(UrnType::Realm, captures); + } + if let Some(captures) = Regex::new(r"atom:btc:container:(.+)(([/$])?.*)")?.captures(urn) { + return process_match(UrnType::Container, captures); + } + if let Some(captures) = Regex::new(r"atom:btc:arc:(.+)(([/$])?.*)")?.captures(urn) { + return process_match(UrnType::Arc, captures); + } + Err(anyhow!("Invalid URN")) +} + +fn process_match(urn_type: UrnType, captures: regex::Captures) -> Result { + Ok(UrnInfo { + urn_type, + identifier: captures[1].to_string(), + path_type: Some(captures.get(3).map_or("", |m| m.as_str()).to_string()), + path: captures.get(2).map(|m| m.as_str().to_string()), + }) +}